5646 lines
234 KiB
Python
5646 lines
234 KiB
Python
import fasteners
|
|
import logging
|
|
import os
|
|
import platform
|
|
import re
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
import types
|
|
import urllib3
|
|
import warnings
|
|
from contextlib import suppress
|
|
from selenium import webdriver
|
|
from selenium.common.exceptions import ElementClickInterceptedException
|
|
from selenium.common.exceptions import InvalidSessionIdException
|
|
from selenium.common.exceptions import SessionNotCreatedException
|
|
from selenium.webdriver.chrome.service import Service as ChromeService
|
|
from selenium.webdriver.common.options import ArgOptions
|
|
from selenium.webdriver.common.service import utils as service_utils
|
|
from selenium.webdriver.edge.service import Service as EdgeService
|
|
from selenium.webdriver.firefox.service import Service as FirefoxService
|
|
from selenium.webdriver.safari.service import Service as SafariService
|
|
from seleniumbase import config as sb_config
|
|
from seleniumbase import decorators
|
|
from seleniumbase import drivers # webdriver storage folder for SeleniumBase
|
|
from seleniumbase import extensions # browser extensions storage folder
|
|
from seleniumbase.config import settings
|
|
from seleniumbase.core import detect_b_ver
|
|
from seleniumbase.core import download_helper
|
|
from seleniumbase.core import proxy_helper
|
|
from seleniumbase.core import sb_driver
|
|
from seleniumbase.core import sb_cdp
|
|
from seleniumbase.fixtures import constants
|
|
from seleniumbase.fixtures import js_utils
|
|
from seleniumbase.fixtures import page_actions
|
|
from seleniumbase.fixtures import shared_utils
|
|
|
|
urllib3.disable_warnings()
|
|
DRIVER_DIR = os.path.dirname(os.path.realpath(drivers.__file__))
|
|
# Make sure that the SeleniumBase DRIVER_DIR is at the top of the System PATH
|
|
# (Changes to the System PATH with os.environ only last during the test run)
|
|
if not os.environ["PATH"].startswith(DRIVER_DIR):
|
|
# Remove existing SeleniumBase DRIVER_DIR from System PATH if present
|
|
os.environ["PATH"] = os.environ["PATH"].replace(DRIVER_DIR, "")
|
|
# If two path separators are next to each other, replace with just one
|
|
os.environ["PATH"] = os.environ["PATH"].replace(
|
|
os.pathsep + os.pathsep, os.pathsep
|
|
)
|
|
# Put the SeleniumBase DRIVER_DIR at the beginning of the System PATH
|
|
os.environ["PATH"] = DRIVER_DIR + os.pathsep + os.environ["PATH"]
|
|
EXTENSIONS_DIR = os.path.dirname(os.path.realpath(extensions.__file__))
|
|
DISABLE_CSP_ZIP_PATH = os.path.join(EXTENSIONS_DIR, "disable_csp.zip")
|
|
AD_BLOCK_ZIP_PATH = os.path.join(EXTENSIONS_DIR, "ad_block.zip")
|
|
RECORDER_ZIP_PATH = os.path.join(EXTENSIONS_DIR, "recorder.zip")
|
|
SBASE_EXT_ZIP_PATH = os.path.join(EXTENSIONS_DIR, "sbase_ext.zip")
|
|
DOWNLOADS_FOLDER = download_helper.get_downloads_folder()
|
|
PROXY_ZIP_PATH = proxy_helper.PROXY_ZIP_PATH
|
|
PROXY_ZIP_LOCK = proxy_helper.PROXY_ZIP_LOCK
|
|
PROXY_DIR_PATH = proxy_helper.PROXY_DIR_PATH
|
|
PROXY_DIR_LOCK = proxy_helper.PROXY_DIR_LOCK
|
|
LOCAL_CHROMEDRIVER = None
|
|
LOCAL_GECKODRIVER = None
|
|
LOCAL_EDGEDRIVER = None
|
|
LOCAL_IEDRIVER = None
|
|
LOCAL_HEADLESS_IEDRIVER = None
|
|
LOCAL_UC_DRIVER = None
|
|
ARCH = platform.architecture()[0]
|
|
IS_ARM_MAC = shared_utils.is_arm_mac()
|
|
IS_MAC = shared_utils.is_mac()
|
|
IS_LINUX = shared_utils.is_linux()
|
|
IS_WINDOWS = shared_utils.is_windows()
|
|
if IS_MAC or IS_LINUX:
|
|
LOCAL_CHROMEDRIVER = DRIVER_DIR + "/chromedriver"
|
|
LOCAL_GECKODRIVER = DRIVER_DIR + "/geckodriver"
|
|
LOCAL_EDGEDRIVER = DRIVER_DIR + "/msedgedriver"
|
|
LOCAL_UC_DRIVER = DRIVER_DIR + "/uc_driver"
|
|
elif IS_WINDOWS:
|
|
LOCAL_EDGEDRIVER = DRIVER_DIR + "/msedgedriver.exe"
|
|
LOCAL_IEDRIVER = DRIVER_DIR + "/IEDriverServer.exe"
|
|
LOCAL_HEADLESS_IEDRIVER = DRIVER_DIR + "/headless_ie_selenium.exe"
|
|
LOCAL_CHROMEDRIVER = DRIVER_DIR + "/chromedriver.exe"
|
|
LOCAL_GECKODRIVER = DRIVER_DIR + "/geckodriver.exe"
|
|
LOCAL_UC_DRIVER = DRIVER_DIR + "/uc_driver.exe"
|
|
else:
|
|
# Cannot determine system
|
|
pass # SeleniumBase will use web drivers from the System PATH by default
|
|
|
|
|
|
def log_d(message):
|
|
"""If setting sb_config.settings.HIDE_DRIVER_DOWNLOADS to True,
|
|
output from driver downloads are logged instead of printed."""
|
|
if (
|
|
hasattr(settings, "HIDE_DRIVER_DOWNLOADS")
|
|
and settings.HIDE_DRIVER_DOWNLOADS
|
|
):
|
|
logging.debug(message)
|
|
else:
|
|
print(message)
|
|
|
|
|
|
def make_driver_executable_if_not(driver_path):
|
|
# Verify driver has executable permissions. If not, add them.
|
|
permissions = oct(os.stat(driver_path)[0])[-3:]
|
|
if "4" in permissions or "6" in permissions:
|
|
# We want at least a '5' or '7' to make sure it's executable
|
|
shared_utils.make_executable(driver_path)
|
|
|
|
|
|
def extend_driver(driver, proxy_auth=False, use_uc=True):
|
|
# Extend the driver with new methods
|
|
driver.default_find_element = driver.find_element
|
|
driver.default_find_elements = driver.find_elements
|
|
DM = sb_driver.DriverMethods(driver)
|
|
driver.find_element = DM.find_element
|
|
driver.find_elements = DM.find_elements
|
|
driver.locator = DM.locator
|
|
page = types.SimpleNamespace()
|
|
page.open = DM.open_url
|
|
page.click = DM.click
|
|
page.click_link = DM.click_link
|
|
page.click_if_visible = DM.click_if_visible
|
|
page.click_active_element = DM.click_active_element
|
|
page.send_keys = DM.send_keys
|
|
page.press_keys = DM.press_keys
|
|
page.type = DM.update_text
|
|
page.submit = DM.submit
|
|
page.assert_element = DM.assert_element_visible
|
|
page.assert_element_present = DM.assert_element_present
|
|
page.assert_element_not_visible = DM.assert_element_not_visible
|
|
page.assert_text = DM.assert_text
|
|
page.assert_exact_text = DM.assert_exact_text
|
|
page.assert_non_empty_text = DM.assert_non_empty_text
|
|
page.assert_text_not_visible = DM.assert_text_not_visible
|
|
page.wait_for_element = DM.wait_for_element
|
|
page.wait_for_text = DM.wait_for_text
|
|
page.wait_for_exact_text = DM.wait_for_exact_text
|
|
page.wait_for_non_empty_text = DM.wait_for_non_empty_text
|
|
page.wait_for_text_not_visible = DM.wait_for_text_not_visible
|
|
page.wait_for_and_accept_alert = DM.wait_for_and_accept_alert
|
|
page.wait_for_and_dismiss_alert = DM.wait_for_and_dismiss_alert
|
|
page.is_element_present = DM.is_element_present
|
|
page.is_element_visible = DM.is_element_visible
|
|
page.is_text_visible = DM.is_text_visible
|
|
page.is_exact_text_visible = DM.is_exact_text_visible
|
|
page.is_attribute_present = DM.is_attribute_present
|
|
page.is_non_empty_text_visible = DM.is_non_empty_text_visible
|
|
page.get_text = DM.get_text
|
|
page.find_element = DM.find_element
|
|
page.find_elements = DM.find_elements
|
|
page.locator = DM.locator
|
|
page.get_current_url = DM.get_current_url
|
|
page.get_page_source = DM.get_page_source
|
|
page.get_title = DM.get_title
|
|
page.get_page_title = DM.get_title
|
|
page.switch_to_default_window = DM.switch_to_default_window
|
|
page.switch_to_newest_window = DM.switch_to_newest_window
|
|
page.open_new_window = DM.open_new_window
|
|
page.open_new_tab = DM.open_new_tab
|
|
page.switch_to_window = DM.switch_to_window
|
|
page.switch_to_tab = DM.switch_to_tab
|
|
page.switch_to_frame = DM.switch_to_frame
|
|
driver.page = page
|
|
js = types.SimpleNamespace()
|
|
js.js_click = DM.js_click
|
|
js.get_active_element_css = DM.get_active_element_css
|
|
js.get_locale_code = DM.get_locale_code
|
|
js.get_origin = DM.get_origin
|
|
js.get_user_agent = DM.get_user_agent
|
|
js.highlight = DM.highlight
|
|
driver.js = js
|
|
driver.open = DM.open_url
|
|
driver.click = DM.click
|
|
driver.click_link = DM.click_link
|
|
driver.click_if_visible = DM.click_if_visible
|
|
driver.click_active_element = DM.click_active_element
|
|
driver.send_keys = DM.send_keys
|
|
driver.press_keys = DM.press_keys
|
|
driver.type = DM.update_text
|
|
driver.submit = DM.submit
|
|
driver.assert_element = DM.assert_element_visible
|
|
driver.assert_element_present = DM.assert_element_present
|
|
driver.assert_element_not_visible = DM.assert_element_not_visible
|
|
driver.assert_text = DM.assert_text
|
|
driver.assert_exact_text = DM.assert_exact_text
|
|
driver.assert_non_empty_text = DM.assert_non_empty_text
|
|
driver.assert_text_not_visible = DM.assert_text_not_visible
|
|
driver.wait_for_element = DM.wait_for_element
|
|
driver.wait_for_element_visible = DM.wait_for_element_visible
|
|
driver.wait_for_element_present = DM.wait_for_element_present
|
|
driver.wait_for_selector = DM.wait_for_selector
|
|
driver.wait_for_text = DM.wait_for_text
|
|
driver.wait_for_exact_text = DM.wait_for_exact_text
|
|
driver.wait_for_non_empty_text = DM.wait_for_non_empty_text
|
|
driver.wait_for_text_not_visible = DM.wait_for_text_not_visible
|
|
driver.wait_for_and_accept_alert = DM.wait_for_and_accept_alert
|
|
driver.wait_for_and_dismiss_alert = DM.wait_for_and_dismiss_alert
|
|
driver.is_element_present = DM.is_element_present
|
|
driver.is_element_visible = DM.is_element_visible
|
|
driver.is_text_visible = DM.is_text_visible
|
|
driver.is_exact_text_visible = DM.is_exact_text_visible
|
|
driver.is_attribute_present = DM.is_attribute_present
|
|
driver.is_non_empty_text_visible = DM.is_non_empty_text_visible
|
|
driver.is_valid_url = DM.is_valid_url
|
|
driver.is_alert_present = DM.is_alert_present
|
|
driver.is_online = DM.is_online
|
|
driver.is_connected = DM.is_connected
|
|
driver.is_uc_mode_active = DM.is_uc_mode_active
|
|
driver.is_cdp_mode_active = DM.is_cdp_mode_active
|
|
driver.js_click = DM.js_click
|
|
driver.get_text = DM.get_text
|
|
driver.get_active_element_css = DM.get_active_element_css
|
|
driver.get_locale_code = DM.get_locale_code
|
|
driver.get_screen_rect = DM.get_screen_rect
|
|
driver.get_origin = DM.get_origin
|
|
driver.get_user_agent = DM.get_user_agent
|
|
driver.get_cookie_string = DM.get_cookie_string
|
|
driver.highlight = DM.highlight
|
|
driver.highlight_click = DM.highlight_click
|
|
driver.highlight_if_visible = DM.highlight_if_visible
|
|
driver.sleep = time.sleep
|
|
driver.get_attribute = DM.get_attribute
|
|
driver.get_parent = DM.get_parent
|
|
driver.get_current_url = DM.get_current_url
|
|
driver.get_page_source = DM.get_page_source
|
|
driver.get_title = DM.get_title
|
|
driver.get_page_title = DM.get_title
|
|
driver.switch_to_default_window = DM.switch_to_default_window
|
|
driver.switch_to_newest_window = DM.switch_to_newest_window
|
|
driver.open_new_window = DM.open_new_window
|
|
driver.open_new_tab = DM.open_new_tab
|
|
driver.switch_to_window = DM.switch_to_window
|
|
driver.switch_to_tab = DM.switch_to_tab
|
|
driver.switch_to_frame = DM.switch_to_frame
|
|
driver.reset_window_size = DM.reset_window_size
|
|
if hasattr(driver, "proxy"):
|
|
driver.set_wire_proxy = DM.set_wire_proxy
|
|
if proxy_auth:
|
|
# Proxy needs a moment to load in Manifest V3
|
|
if use_uc:
|
|
time.sleep(0.12)
|
|
else:
|
|
time.sleep(0.22)
|
|
return driver
|
|
|
|
|
|
@decorators.rate_limited(4)
|
|
def requests_get(url, proxy_string=None):
|
|
import requests
|
|
|
|
protocol = "http"
|
|
proxies = None
|
|
if proxy_string:
|
|
if proxy_string.endswith(":443"):
|
|
protocol = "https"
|
|
elif "socks4" in proxy_string:
|
|
protocol = "socks4"
|
|
elif "socks5" in proxy_string:
|
|
protocol = "socks5"
|
|
proxies = {protocol: proxy_string}
|
|
response = None
|
|
try:
|
|
response = requests.get(url, proxies=proxies, timeout=1.25)
|
|
except Exception:
|
|
# Prevent SSLCertVerificationError / CERTIFICATE_VERIFY_FAILED
|
|
url = url.replace("https://", "http://")
|
|
time.sleep(0.04)
|
|
response = requests.get(url, proxies=proxies, timeout=2.75)
|
|
return response
|
|
|
|
|
|
def get_latest_chromedriver_version():
|
|
from seleniumbase.console_scripts import sb_install
|
|
return sb_install.get_latest_stable_chromedriver_version()
|
|
|
|
|
|
def chromedriver_on_path():
|
|
paths = os.environ["PATH"].split(os.pathsep)
|
|
for path in paths:
|
|
if (
|
|
not IS_WINDOWS
|
|
and os.path.exists(os.path.join(path, "chromedriver"))
|
|
):
|
|
return os.path.join(path, "chromedriver")
|
|
elif (
|
|
IS_WINDOWS
|
|
and os.path.exists(os.path.join(path, "chromedriver.exe"))
|
|
):
|
|
return os.path.join(path, "chromedriver.exe")
|
|
return None
|
|
|
|
|
|
def get_uc_driver_version(full=False):
|
|
uc_driver_version = None
|
|
if os.path.exists(LOCAL_UC_DRIVER):
|
|
with suppress(Exception):
|
|
output = subprocess.check_output(
|
|
'"%s" --version' % LOCAL_UC_DRIVER, shell=True
|
|
)
|
|
if IS_WINDOWS:
|
|
output = output.decode("latin1")
|
|
else:
|
|
output = output.decode("utf-8")
|
|
full_version = output.split(" ")[1]
|
|
output = output.split(" ")[1].split(".")[0]
|
|
if int(output) >= 72:
|
|
if full:
|
|
uc_driver_version = full_version
|
|
else:
|
|
uc_driver_version = output
|
|
return uc_driver_version
|
|
|
|
|
|
def find_chromedriver_version_to_use(use_version, driver_version):
|
|
# Note: https://chromedriver.chromium.org/downloads stops at 114.
|
|
# Future drivers are part of the Chrome-for-Testing collection.
|
|
if (
|
|
driver_version
|
|
and str(driver_version).split(".")[0].isdigit()
|
|
and int(str(driver_version).split(".")[0]) >= 72
|
|
):
|
|
use_version = str(driver_version)
|
|
elif driver_version and not str(driver_version).split(".")[0].isdigit():
|
|
from seleniumbase.console_scripts import sb_install
|
|
driver_version = driver_version.lower()
|
|
if driver_version == "stable" or driver_version == "latest":
|
|
use_version = sb_install.get_latest_stable_chromedriver_version()
|
|
elif driver_version == "beta":
|
|
use_version = sb_install.get_latest_beta_chromedriver_version()
|
|
elif driver_version == "dev":
|
|
use_version = sb_install.get_latest_dev_chromedriver_version()
|
|
elif driver_version == "canary":
|
|
use_version = sb_install.get_latest_canary_chromedriver_version()
|
|
elif driver_version == "previous" or driver_version == "latest-1":
|
|
use_version = sb_install.get_latest_stable_chromedriver_version()
|
|
use_version = str(int(use_version.split(".")[0]) - 1)
|
|
elif driver_version == "mlatest":
|
|
if use_version.split(".")[0].isdigit():
|
|
major = use_version.split(".")[0]
|
|
if int(major) >= 115:
|
|
use_version = (
|
|
sb_install.get_cft_latest_version_from_milestone(major)
|
|
)
|
|
return use_version
|
|
|
|
|
|
def find_edgedriver_version_to_use(use_version, driver_version):
|
|
if (
|
|
driver_version
|
|
and str(driver_version).split(".")[0].isdigit()
|
|
and int(str(driver_version).split(".")[0]) >= 80
|
|
):
|
|
use_version = str(driver_version)
|
|
return use_version
|
|
|
|
|
|
def has_captcha(text):
|
|
if (
|
|
"<title>403 Forbidden</title>" in text
|
|
or "Permission Denied</title>" in text
|
|
or 'id="challenge-error-text"' in text
|
|
or "<title>Just a moment..." in text
|
|
or 'action="/?__cf_chl_f_tk' in text
|
|
or 'id="challenge-widget-' in text
|
|
or 'src="chromedriver.js"' in text
|
|
or 'class="g-recaptcha"' in text
|
|
or 'content="Pixelscan"' in text
|
|
or 'id="challenge-form"' in text
|
|
or "/challenge-platform" in text
|
|
or "window._cf_chl_opt" in text
|
|
or "/recaptcha/api.js" in text
|
|
or "/turnstile/" in text
|
|
):
|
|
return True
|
|
return False
|
|
|
|
|
|
def __is_cdp_swap_needed(driver):
|
|
"""If the driver is disconnected, use a CDP method when available."""
|
|
return shared_utils.is_cdp_swap_needed(driver)
|
|
|
|
|
|
def uc_special_open_if_cf(
|
|
driver,
|
|
url,
|
|
proxy_string=None,
|
|
mobile_emulator=None,
|
|
device_width=None,
|
|
device_height=None,
|
|
device_pixel_ratio=None,
|
|
):
|
|
if url.startswith("http:") or url.startswith("https:"):
|
|
special = False
|
|
with suppress(Exception):
|
|
req_get = requests_get(url, proxy_string)
|
|
status_str = str(req_get.status_code)
|
|
if (
|
|
status_str.startswith("3")
|
|
or status_str.startswith("4")
|
|
or status_str.startswith("5")
|
|
or has_captcha(req_get.text)
|
|
):
|
|
special = True
|
|
if status_str == "403" or status_str == "429":
|
|
time.sleep(0.06) # Forbidden / Blocked! (Wait first!)
|
|
if special:
|
|
time.sleep(0.05)
|
|
with driver:
|
|
driver.execute_script('window.open("%s","_blank");' % url)
|
|
driver.close()
|
|
if mobile_emulator:
|
|
page_actions.switch_to_window(
|
|
driver, driver.window_handles[-1], 2
|
|
)
|
|
uc_metrics = {}
|
|
if (
|
|
isinstance(device_width, int)
|
|
and isinstance(device_height, int)
|
|
and isinstance(device_pixel_ratio, (int, float))
|
|
):
|
|
uc_metrics["width"] = device_width
|
|
uc_metrics["height"] = device_height
|
|
uc_metrics["pixelRatio"] = device_pixel_ratio
|
|
else:
|
|
uc_metrics["width"] = constants.Mobile.WIDTH
|
|
uc_metrics["height"] = constants.Mobile.HEIGHT
|
|
uc_metrics["pixelRatio"] = constants.Mobile.RATIO
|
|
set_device_metrics_override = dict(
|
|
{
|
|
"width": uc_metrics["width"],
|
|
"height": uc_metrics["height"],
|
|
"deviceScaleFactor": uc_metrics["pixelRatio"],
|
|
"mobile": True
|
|
}
|
|
)
|
|
with suppress(Exception):
|
|
driver.execute_cdp_cmd(
|
|
'Emulation.setDeviceMetricsOverride',
|
|
set_device_metrics_override
|
|
)
|
|
if not mobile_emulator:
|
|
page_actions.switch_to_window(
|
|
driver, driver.window_handles[-1], 2
|
|
)
|
|
else:
|
|
driver.default_get(url) # The original one
|
|
else:
|
|
driver.default_get(url) # The original one
|
|
return None
|
|
|
|
|
|
def uc_open(driver, url):
|
|
url = shared_utils.fix_url_as_needed(url)
|
|
if __is_cdp_swap_needed(driver):
|
|
driver.cdp.get(url)
|
|
time.sleep(0.3)
|
|
return
|
|
if (url.startswith("http:") or url.startswith("https:")):
|
|
with driver:
|
|
script = 'window.location.href = "%s";' % url
|
|
js_utils.call_me_later(driver, script, 5)
|
|
else:
|
|
driver.default_get(url) # The original one
|
|
return None
|
|
|
|
|
|
def uc_open_with_tab(driver, url):
|
|
url = shared_utils.fix_url_as_needed(url)
|
|
if __is_cdp_swap_needed(driver):
|
|
driver.cdp.get(url)
|
|
time.sleep(0.3)
|
|
return
|
|
if (url.startswith("http:") or url.startswith("https:")):
|
|
with driver:
|
|
driver.execute_script('window.open("%s","_blank");' % url)
|
|
driver.close()
|
|
page_actions.switch_to_window(driver, driver.window_handles[-1], 2)
|
|
else:
|
|
driver.default_get(url) # The original one
|
|
return None
|
|
|
|
|
|
def uc_open_with_reconnect(driver, url, reconnect_time=None):
|
|
"""Open a url, disconnect chromedriver, wait, and reconnect."""
|
|
url = shared_utils.fix_url_as_needed(url)
|
|
if __is_cdp_swap_needed(driver):
|
|
driver.cdp.get(url)
|
|
time.sleep(0.3)
|
|
return
|
|
if not reconnect_time:
|
|
reconnect_time = constants.UC.RECONNECT_TIME
|
|
if (url.startswith("http:") or url.startswith("https:")):
|
|
script = 'window.open("%s","_blank");' % url
|
|
driver.execute_script(script)
|
|
time.sleep(0.05)
|
|
driver.close()
|
|
if reconnect_time == "disconnect":
|
|
driver.disconnect()
|
|
time.sleep(0.008)
|
|
else:
|
|
driver.reconnect(reconnect_time)
|
|
time.sleep(0.004)
|
|
try:
|
|
page_actions.switch_to_window(
|
|
driver, driver.window_handles[-1], 2
|
|
)
|
|
except InvalidSessionIdException:
|
|
time.sleep(0.05)
|
|
page_actions.switch_to_window(
|
|
driver, driver.window_handles[-1], 2
|
|
)
|
|
else:
|
|
driver.default_get(url) # The original one
|
|
return None
|
|
|
|
|
|
def uc_open_with_cdp_mode(driver, url=None):
|
|
import asyncio
|
|
from seleniumbase.undetected.cdp_driver import cdp_util
|
|
|
|
current_url = None
|
|
try:
|
|
current_url = driver.current_url
|
|
except Exception:
|
|
driver.connect()
|
|
current_url = driver.current_url
|
|
url_protocol = current_url.split(":")[0]
|
|
if url_protocol not in ["about", "data", "chrome"]:
|
|
script = 'window.open("data:,","_blank");'
|
|
js_utils.call_me_later(driver, script, 3)
|
|
time.sleep(0.012)
|
|
driver.close()
|
|
driver.disconnect()
|
|
|
|
cdp_details = driver._get_cdp_details()
|
|
cdp_host = cdp_details[1].split("://")[1].split(":")[0]
|
|
cdp_port = int(cdp_details[1].split("://")[1].split(":")[1].split("/")[0])
|
|
|
|
url = shared_utils.fix_url_as_needed(url)
|
|
url_protocol = url.split(":")[0]
|
|
safe_url = True
|
|
if url_protocol not in ["about", "data", "chrome"]:
|
|
safe_url = False
|
|
|
|
headless = False
|
|
headed = None
|
|
xvfb = None
|
|
if hasattr(sb_config, "headless"):
|
|
headless = sb_config.headless
|
|
if hasattr(sb_config, "headed"):
|
|
headed = sb_config.headed
|
|
if hasattr(sb_config, "xvfb"):
|
|
xvfb = sb_config.xvfb
|
|
|
|
loop = asyncio.new_event_loop()
|
|
asyncio.set_event_loop(loop)
|
|
driver.cdp_base = loop.run_until_complete(
|
|
cdp_util.start(
|
|
host=cdp_host,
|
|
port=cdp_port,
|
|
headless=headless,
|
|
headed=headed,
|
|
xvfb=xvfb,
|
|
)
|
|
)
|
|
loop.run_until_complete(driver.cdp_base.wait(0))
|
|
|
|
gui_lock = fasteners.InterProcessLock(constants.MultiBrowser.PYAUTOGUILOCK)
|
|
|
|
if (
|
|
"chrome-extension://" in str(driver.cdp_base.main_tab)
|
|
and len(driver.cdp_base.tabs) >= 2
|
|
):
|
|
with suppress(Exception):
|
|
loop.run_until_complete(driver.cdp_base.main_tab.close())
|
|
|
|
for tab in driver.cdp_base.tabs[-1::-1]:
|
|
if "chrome-extension://" not in str(tab):
|
|
with gui_lock:
|
|
with suppress(Exception):
|
|
shared_utils.make_writable(
|
|
constants.MultiBrowser.PYAUTOGUILOCK
|
|
)
|
|
loop.run_until_complete(tab.activate())
|
|
break
|
|
|
|
page_tab = None
|
|
if "chrome-extension://" not in str(driver.cdp_base.tabs[-1]):
|
|
page_tab = driver.cdp_base.tabs[-1]
|
|
else:
|
|
for tab in driver.cdp_base.tabs:
|
|
if "chrome-extension://" not in str(tab):
|
|
page_tab = tab
|
|
break
|
|
if page_tab:
|
|
loop.run_until_complete(page_tab.aopen())
|
|
with gui_lock:
|
|
with suppress(Exception):
|
|
shared_utils.make_writable(
|
|
constants.MultiBrowser.PYAUTOGUILOCK
|
|
)
|
|
loop.run_until_complete(page_tab.activate())
|
|
|
|
loop.run_until_complete(driver.cdp_base.update_targets())
|
|
page = loop.run_until_complete(driver.cdp_base.get(url))
|
|
with gui_lock:
|
|
with suppress(Exception):
|
|
shared_utils.make_writable(constants.MultiBrowser.PYAUTOGUILOCK)
|
|
loop.run_until_complete(page.activate())
|
|
loop.run_until_complete(page.wait())
|
|
if not safe_url:
|
|
time.sleep(constants.UC.CDP_MODE_OPEN_WAIT)
|
|
if IS_WINDOWS:
|
|
time.sleep(constants.UC.EXTRA_WINDOWS_WAIT)
|
|
else:
|
|
time.sleep(0.012)
|
|
cdp = types.SimpleNamespace()
|
|
CDPM = sb_cdp.CDPMethods(loop, page, driver)
|
|
cdp.get = CDPM.get
|
|
cdp.open = CDPM.open
|
|
cdp.reload = CDPM.reload
|
|
cdp.refresh = CDPM.refresh
|
|
cdp.add_handler = CDPM.add_handler
|
|
cdp.get_event_loop = CDPM.get_event_loop
|
|
cdp.find_element = CDPM.find_element
|
|
cdp.find = CDPM.find_element
|
|
cdp.locator = CDPM.find_element
|
|
cdp.find_element_by_text = CDPM.find_element_by_text
|
|
cdp.find_all = CDPM.find_all
|
|
cdp.find_elements_by_text = CDPM.find_elements_by_text
|
|
cdp.select = CDPM.select
|
|
cdp.select_all = CDPM.select_all
|
|
cdp.find_elements = CDPM.find_elements
|
|
cdp.find_visible_elements = CDPM.find_visible_elements
|
|
cdp.click_nth_element = CDPM.click_nth_element
|
|
cdp.click_nth_visible_element = CDPM.click_nth_visible_element
|
|
cdp.click_link = CDPM.click_link
|
|
cdp.go_back = CDPM.go_back
|
|
cdp.go_forward = CDPM.go_forward
|
|
cdp.get_navigation_history = CDPM.get_navigation_history
|
|
cdp.tile_windows = CDPM.tile_windows
|
|
cdp.get_all_cookies = CDPM.get_all_cookies
|
|
cdp.set_all_cookies = CDPM.set_all_cookies
|
|
cdp.save_cookies = CDPM.save_cookies
|
|
cdp.load_cookies = CDPM.load_cookies
|
|
cdp.clear_cookies = CDPM.clear_cookies
|
|
cdp.sleep = CDPM.sleep
|
|
cdp.bring_active_window_to_front = CDPM.bring_active_window_to_front
|
|
cdp.bring_to_front = CDPM.bring_active_window_to_front
|
|
cdp.get_active_element = CDPM.get_active_element
|
|
cdp.get_active_element_css = CDPM.get_active_element_css
|
|
cdp.click = CDPM.click
|
|
cdp.click_active_element = CDPM.click_active_element
|
|
cdp.click_if_visible = CDPM.click_if_visible
|
|
cdp.click_visible_elements = CDPM.click_visible_elements
|
|
cdp.mouse_click = CDPM.mouse_click
|
|
cdp.get_parent = CDPM.get_parent
|
|
cdp.remove_element = CDPM.remove_element
|
|
cdp.remove_from_dom = CDPM.remove_from_dom
|
|
cdp.remove_elements = CDPM.remove_elements
|
|
cdp.send_keys = CDPM.send_keys
|
|
cdp.press_keys = CDPM.press_keys
|
|
cdp.type = CDPM.type
|
|
cdp.set_value = CDPM.set_value
|
|
cdp.submit = CDPM.submit
|
|
cdp.evaluate = CDPM.evaluate
|
|
cdp.js_dumps = CDPM.js_dumps
|
|
cdp.maximize = CDPM.maximize
|
|
cdp.minimize = CDPM.minimize
|
|
cdp.medimize = CDPM.medimize
|
|
cdp.set_window_rect = CDPM.set_window_rect
|
|
cdp.reset_window_size = CDPM.reset_window_size
|
|
cdp.set_locale = CDPM.set_locale
|
|
cdp.set_local_storage_item = CDPM.set_local_storage_item
|
|
cdp.set_session_storage_item = CDPM.set_session_storage_item
|
|
cdp.set_attributes = CDPM.set_attributes
|
|
cdp.gui_press_key = CDPM.gui_press_key
|
|
cdp.gui_press_keys = CDPM.gui_press_keys
|
|
cdp.gui_write = CDPM.gui_write
|
|
cdp.gui_click_x_y = CDPM.gui_click_x_y
|
|
cdp.gui_click_element = CDPM.gui_click_element
|
|
cdp.gui_drag_drop_points = CDPM.gui_drag_drop_points
|
|
cdp.gui_drag_and_drop = CDPM.gui_drag_and_drop
|
|
cdp.gui_hover_x_y = CDPM.gui_hover_x_y
|
|
cdp.gui_hover_element = CDPM.gui_hover_element
|
|
cdp.gui_hover_and_click = CDPM.gui_hover_and_click
|
|
cdp.internalize_links = CDPM.internalize_links
|
|
cdp.open_new_window = CDPM.open_new_window
|
|
cdp.switch_to_window = CDPM.switch_to_window
|
|
cdp.switch_to_newest_window = CDPM.switch_to_newest_window
|
|
cdp.open_new_tab = CDPM.open_new_tab
|
|
cdp.switch_to_tab = CDPM.switch_to_tab
|
|
cdp.switch_to_newest_tab = CDPM.switch_to_newest_tab
|
|
cdp.close_active_tab = CDPM.close_active_tab
|
|
cdp.get_active_tab = CDPM.get_active_tab
|
|
cdp.get_tabs = CDPM.get_tabs
|
|
cdp.get_window = CDPM.get_window
|
|
cdp.get_element_attributes = CDPM.get_element_attributes
|
|
cdp.get_element_attribute = CDPM.get_element_attribute
|
|
cdp.get_attribute = CDPM.get_attribute
|
|
cdp.get_element_html = CDPM.get_element_html
|
|
cdp.get_element_rect = CDPM.get_element_rect
|
|
cdp.get_element_size = CDPM.get_element_size
|
|
cdp.get_element_position = CDPM.get_element_position
|
|
cdp.get_gui_element_rect = CDPM.get_gui_element_rect
|
|
cdp.get_gui_element_center = CDPM.get_gui_element_center
|
|
cdp.get_page_source = CDPM.get_page_source
|
|
cdp.get_user_agent = CDPM.get_user_agent
|
|
cdp.get_cookie_string = CDPM.get_cookie_string
|
|
cdp.get_locale_code = CDPM.get_locale_code
|
|
cdp.get_local_storage_item = CDPM.get_local_storage_item
|
|
cdp.get_session_storage_item = CDPM.get_session_storage_item
|
|
cdp.get_text = CDPM.get_text
|
|
cdp.get_title = CDPM.get_title
|
|
cdp.get_page_title = CDPM.get_title
|
|
cdp.get_current_url = CDPM.get_current_url
|
|
cdp.get_origin = CDPM.get_origin
|
|
cdp.get_nested_element = CDPM.get_nested_element
|
|
cdp.get_document = CDPM.get_document
|
|
cdp.get_flattened_document = CDPM.get_flattened_document
|
|
cdp.get_screen_rect = CDPM.get_screen_rect
|
|
cdp.get_window_rect = CDPM.get_window_rect
|
|
cdp.get_window_size = CDPM.get_window_size
|
|
cdp.nested_click = CDPM.nested_click
|
|
cdp.select_option_by_text = CDPM.select_option_by_text
|
|
cdp.flash = CDPM.flash
|
|
cdp.highlight = CDPM.highlight
|
|
cdp.focus = CDPM.focus
|
|
cdp.highlight_overlay = CDPM.highlight_overlay
|
|
cdp.get_window_position = CDPM.get_window_position
|
|
cdp.check_if_unchecked = CDPM.check_if_unchecked
|
|
cdp.uncheck_if_checked = CDPM.uncheck_if_checked
|
|
cdp.select_if_unselected = CDPM.select_if_unselected
|
|
cdp.unselect_if_selected = CDPM.unselect_if_selected
|
|
cdp.is_checked = CDPM.is_checked
|
|
cdp.is_selected = CDPM.is_selected
|
|
cdp.is_element_present = CDPM.is_element_present
|
|
cdp.is_element_visible = CDPM.is_element_visible
|
|
cdp.is_text_visible = CDPM.is_text_visible
|
|
cdp.is_exact_text_visible = CDPM.is_exact_text_visible
|
|
cdp.wait_for_text = CDPM.wait_for_text
|
|
cdp.wait_for_text_not_visible = CDPM.wait_for_text_not_visible
|
|
cdp.wait_for_element_visible = CDPM.wait_for_element_visible
|
|
cdp.wait_for_element_not_visible = CDPM.wait_for_element_not_visible
|
|
cdp.wait_for_element_absent = CDPM.wait_for_element_absent
|
|
cdp.assert_element = CDPM.assert_element
|
|
cdp.assert_element_visible = CDPM.assert_element_visible
|
|
cdp.assert_element_present = CDPM.assert_element_present
|
|
cdp.assert_element_absent = CDPM.assert_element_absent
|
|
cdp.assert_element_not_visible = CDPM.assert_element_not_visible
|
|
cdp.assert_element_attribute = CDPM.assert_element_attribute
|
|
cdp.assert_title = CDPM.assert_title
|
|
cdp.assert_title_contains = CDPM.assert_title_contains
|
|
cdp.assert_url = CDPM.assert_url
|
|
cdp.assert_url_contains = CDPM.assert_url_contains
|
|
cdp.assert_text = CDPM.assert_text
|
|
cdp.assert_exact_text = CDPM.assert_exact_text
|
|
cdp.assert_text_not_visible = CDPM.assert_text_not_visible
|
|
cdp.assert_true = CDPM.assert_true
|
|
cdp.assert_false = CDPM.assert_false
|
|
cdp.assert_equal = CDPM.assert_equal
|
|
cdp.assert_not_equal = CDPM.assert_not_equal
|
|
cdp.assert_in = CDPM.assert_in
|
|
cdp.assert_not_in = CDPM.assert_not_in
|
|
cdp.scroll_into_view = CDPM.scroll_into_view
|
|
cdp.scroll_to_y = CDPM.scroll_to_y
|
|
cdp.scroll_to_top = CDPM.scroll_to_top
|
|
cdp.scroll_to_bottom = CDPM.scroll_to_bottom
|
|
cdp.scroll_up = CDPM.scroll_up
|
|
cdp.scroll_down = CDPM.scroll_down
|
|
cdp.save_screenshot = CDPM.save_screenshot
|
|
cdp.page = page # async world
|
|
cdp.driver = driver.cdp_base # async world
|
|
cdp.tab = cdp.page # shortcut (original)
|
|
cdp.browser = driver.cdp_base # shortcut (original)
|
|
cdp.util = cdp_util # shortcut (original)
|
|
core_items = types.SimpleNamespace()
|
|
core_items.browser = cdp.browser
|
|
core_items.tab = cdp.tab
|
|
core_items.util = cdp.util
|
|
cdp._swap_driver = CDPM._swap_driver
|
|
cdp.core = core_items
|
|
cdp.loop = cdp.get_event_loop()
|
|
driver.cdp = cdp
|
|
driver._is_using_cdp = True
|
|
|
|
|
|
def uc_activate_cdp_mode(driver, url=None):
|
|
uc_open_with_cdp_mode(driver, url=url)
|
|
|
|
|
|
def uc_open_with_disconnect(driver, url, timeout=None):
|
|
"""Open a url and disconnect chromedriver.
|
|
Then waits for the duration of the timeout.
|
|
Note: You can't perform Selenium actions again
|
|
until after you've called driver.connect()."""
|
|
url = shared_utils.fix_url_as_needed(url)
|
|
if __is_cdp_swap_needed(driver):
|
|
driver.cdp.get(url)
|
|
time.sleep(0.3)
|
|
return
|
|
if not driver.is_connected():
|
|
driver.connect()
|
|
if (url.startswith("http:") or url.startswith("https:")):
|
|
script = 'window.open("%s","_blank");' % url
|
|
driver.execute_script(script)
|
|
time.sleep(0.05)
|
|
driver.close()
|
|
driver.disconnect()
|
|
min_timeout = 0.008
|
|
if timeout and not str(timeout).replace(".", "", 1).isdigit():
|
|
timeout = min_timeout
|
|
if not timeout or timeout < min_timeout:
|
|
timeout = min_timeout
|
|
time.sleep(timeout)
|
|
else:
|
|
driver.default_get(url) # The original one
|
|
return None
|
|
|
|
|
|
def uc_click(
|
|
driver,
|
|
selector,
|
|
by="css selector",
|
|
timeout=settings.SMALL_TIMEOUT,
|
|
reconnect_time=None,
|
|
):
|
|
if __is_cdp_swap_needed(driver):
|
|
driver.cdp.click(selector)
|
|
return
|
|
with suppress(Exception):
|
|
rct = float(by) # Add shortcut: driver.uc_click(selector, RCT)
|
|
if not reconnect_time:
|
|
reconnect_time = rct
|
|
by = "css selector"
|
|
element = driver.wait_for_selector(selector, by=by, timeout=timeout)
|
|
tag_name = element.tag_name
|
|
if not tag_name == "span" and not tag_name == "input": # Must be "visible"
|
|
element = driver.wait_for_element(selector, by=by, timeout=timeout)
|
|
try:
|
|
element.uc_click(
|
|
driver,
|
|
selector,
|
|
by=by,
|
|
reconnect_time=reconnect_time,
|
|
tag_name=tag_name,
|
|
)
|
|
except ElementClickInterceptedException:
|
|
time.sleep(0.16)
|
|
driver.js_click(selector, by=by, timeout=timeout)
|
|
if not reconnect_time:
|
|
driver.reconnect(0.1)
|
|
else:
|
|
driver.reconnect(reconnect_time)
|
|
|
|
|
|
def verify_pyautogui_has_a_headed_browser(driver):
|
|
"""PyAutoGUI requires a headed browser so that it can
|
|
focus on the correct element when performing actions."""
|
|
if hasattr(driver, "_is_hidden") and driver._is_hidden:
|
|
raise Exception(
|
|
"PyAutoGUI can't be used in headless mode!"
|
|
)
|
|
|
|
|
|
def __install_pyautogui_if_missing():
|
|
try:
|
|
import pyautogui
|
|
with suppress(Exception):
|
|
use_pyautogui_ver = constants.PyAutoGUI.VER
|
|
if pyautogui.__version__ != use_pyautogui_ver:
|
|
del pyautogui
|
|
shared_utils.pip_install(
|
|
"pyautogui", version=use_pyautogui_ver
|
|
)
|
|
import pyautogui
|
|
except Exception:
|
|
print("\nPyAutoGUI required! Installing now...")
|
|
shared_utils.pip_install(
|
|
"pyautogui", version=constants.PyAutoGUI.VER
|
|
)
|
|
try:
|
|
import pyautogui
|
|
except Exception:
|
|
if (
|
|
IS_LINUX
|
|
and hasattr(sb_config, "xvfb")
|
|
and hasattr(sb_config, "headed")
|
|
and hasattr(sb_config, "headless")
|
|
and hasattr(sb_config, "headless2")
|
|
and (not sb_config.headed or sb_config.xvfb)
|
|
and not (sb_config.headless or sb_config.headless2)
|
|
):
|
|
from sbvirtualdisplay import Display
|
|
xvfb_width = 1366
|
|
xvfb_height = 768
|
|
if (
|
|
hasattr(sb_config, "_xvfb_width")
|
|
and sb_config._xvfb_width
|
|
and isinstance(sb_config._xvfb_width, int)
|
|
and hasattr(sb_config, "_xvfb_height")
|
|
and sb_config._xvfb_height
|
|
and isinstance(sb_config._xvfb_height, int)
|
|
):
|
|
xvfb_width = sb_config._xvfb_width
|
|
xvfb_height = sb_config._xvfb_height
|
|
if xvfb_width < 1024:
|
|
xvfb_width = 1024
|
|
sb_config._xvfb_width = xvfb_width
|
|
if xvfb_height < 768:
|
|
xvfb_height = 768
|
|
sb_config._xvfb_height = xvfb_height
|
|
with suppress(Exception):
|
|
_xvfb_display = Display(
|
|
visible=True,
|
|
size=(xvfb_width, xvfb_height),
|
|
backend="xvfb",
|
|
use_xauth=True,
|
|
)
|
|
_xvfb_display.start()
|
|
sb_config._virtual_display = _xvfb_display
|
|
sb_config.headless_active = True
|
|
if (
|
|
hasattr(sb_config, "reuse_session")
|
|
and sb_config.reuse_session
|
|
and hasattr(sb_config, "_vd_list")
|
|
and isinstance(sb_config._vd_list, list)
|
|
):
|
|
sb_config._vd_list.append(_xvfb_display)
|
|
|
|
|
|
def install_pyautogui_if_missing(driver):
|
|
verify_pyautogui_has_a_headed_browser(driver)
|
|
pip_find_lock = fasteners.InterProcessLock(
|
|
constants.PipInstall.FINDLOCK
|
|
)
|
|
try:
|
|
with pip_find_lock:
|
|
pass
|
|
except Exception:
|
|
# Since missing permissions, skip the locks
|
|
__install_pyautogui_if_missing()
|
|
return
|
|
with pip_find_lock: # Prevent issues with multiple processes
|
|
with suppress(Exception):
|
|
shared_utils.make_writable(constants.PipInstall.FINDLOCK)
|
|
__install_pyautogui_if_missing()
|
|
|
|
|
|
def get_configured_pyautogui(pyautogui_copy):
|
|
if (
|
|
IS_LINUX
|
|
and hasattr(pyautogui_copy, "_pyautogui_x11")
|
|
and "DISPLAY" in os.environ.keys()
|
|
):
|
|
if (
|
|
hasattr(sb_config, "_pyautogui_x11_display")
|
|
and sb_config._pyautogui_x11_display
|
|
and hasattr(pyautogui_copy._pyautogui_x11, "_display")
|
|
and (
|
|
sb_config._pyautogui_x11_display
|
|
== pyautogui_copy._pyautogui_x11._display
|
|
)
|
|
):
|
|
pass
|
|
else:
|
|
import Xlib.display
|
|
pyautogui_copy._pyautogui_x11._display = (
|
|
Xlib.display.Display(os.environ['DISPLAY'])
|
|
)
|
|
sb_config._pyautogui_x11_display = (
|
|
pyautogui_copy._pyautogui_x11._display
|
|
)
|
|
return pyautogui_copy
|
|
|
|
|
|
def uc_gui_press_key(driver, key):
|
|
install_pyautogui_if_missing(driver)
|
|
import pyautogui
|
|
pyautogui = get_configured_pyautogui(pyautogui)
|
|
gui_lock = fasteners.InterProcessLock(
|
|
constants.MultiBrowser.PYAUTOGUILOCK
|
|
)
|
|
with gui_lock:
|
|
pyautogui.press(key)
|
|
|
|
|
|
def uc_gui_press_keys(driver, keys):
|
|
install_pyautogui_if_missing(driver)
|
|
import pyautogui
|
|
pyautogui = get_configured_pyautogui(pyautogui)
|
|
gui_lock = fasteners.InterProcessLock(
|
|
constants.MultiBrowser.PYAUTOGUILOCK
|
|
)
|
|
with gui_lock:
|
|
for key in keys:
|
|
pyautogui.press(key)
|
|
|
|
|
|
def uc_gui_write(driver, text):
|
|
install_pyautogui_if_missing(driver)
|
|
import pyautogui
|
|
pyautogui = get_configured_pyautogui(pyautogui)
|
|
gui_lock = fasteners.InterProcessLock(
|
|
constants.MultiBrowser.PYAUTOGUILOCK
|
|
)
|
|
with gui_lock:
|
|
pyautogui.write(text)
|
|
|
|
|
|
def get_gui_element_position(driver, selector):
|
|
if __is_cdp_swap_needed(driver):
|
|
element_rect = driver.cdp.get_gui_element_rect(selector)
|
|
return (element_rect["x"], element_rect["y"])
|
|
element = driver.wait_for_element_present(selector, timeout=3)
|
|
element_rect = element.rect
|
|
window_rect = driver.get_window_rect()
|
|
window_bottom_y = window_rect["y"] + window_rect["height"]
|
|
viewport_height = driver.execute_script("return window.innerHeight;")
|
|
viewport_x = window_rect["x"] + element_rect["x"]
|
|
viewport_y = window_bottom_y - viewport_height + element_rect["y"]
|
|
y_scroll_offset = driver.execute_script("return window.pageYOffset;")
|
|
viewport_y = viewport_y - y_scroll_offset
|
|
return (viewport_x, viewport_y)
|
|
|
|
|
|
def _uc_gui_click_x_y(driver, x, y, timeframe=0.25, uc_lock=False):
|
|
install_pyautogui_if_missing(driver)
|
|
import pyautogui
|
|
pyautogui = get_configured_pyautogui(pyautogui)
|
|
screen_width, screen_height = pyautogui.size()
|
|
if x < 0 or y < 0 or x > screen_width or y > screen_height:
|
|
raise Exception(
|
|
"PyAutoGUI cannot click on point (%s, %s)"
|
|
" outside screen. (Width: %s, Height: %s)"
|
|
% (x, y, screen_width, screen_height)
|
|
)
|
|
if uc_lock:
|
|
gui_lock = fasteners.InterProcessLock(
|
|
constants.MultiBrowser.PYAUTOGUILOCK
|
|
)
|
|
with gui_lock: # Prevent issues with multiple processes
|
|
pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad)
|
|
if timeframe >= 0.25:
|
|
time.sleep(0.056) # Wait if moving at human-speed
|
|
if "--debug" in sys.argv:
|
|
print(" <DEBUG> pyautogui.click(%s, %s)" % (x, y))
|
|
pyautogui.click(x=x, y=y)
|
|
else:
|
|
# Called from a method where the gui_lock is already active
|
|
pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad)
|
|
if timeframe >= 0.25:
|
|
time.sleep(0.056) # Wait if moving at human-speed
|
|
if "--debug" in sys.argv:
|
|
print(" <DEBUG> pyautogui.click(%s, %s)" % (x, y))
|
|
pyautogui.click(x=x, y=y)
|
|
|
|
|
|
def uc_gui_click_x_y(driver, x, y, timeframe=0.25):
|
|
gui_lock = fasteners.InterProcessLock(
|
|
constants.MultiBrowser.PYAUTOGUILOCK
|
|
)
|
|
with gui_lock: # Prevent issues with multiple processes
|
|
install_pyautogui_if_missing(driver)
|
|
import pyautogui
|
|
pyautogui = get_configured_pyautogui(pyautogui)
|
|
connected = True
|
|
width_ratio = 1.0
|
|
if IS_WINDOWS:
|
|
connected = driver.is_connected()
|
|
if (
|
|
not connected
|
|
and (
|
|
not hasattr(sb_config, "_saved_width_ratio")
|
|
or not sb_config._saved_width_ratio
|
|
)
|
|
and not __is_cdp_swap_needed(driver)
|
|
):
|
|
driver.reconnect(0.1)
|
|
if IS_WINDOWS and not __is_cdp_swap_needed(driver):
|
|
window_rect = driver.get_window_rect()
|
|
width = window_rect["width"]
|
|
height = window_rect["height"]
|
|
win_x = window_rect["x"]
|
|
win_y = window_rect["y"]
|
|
scr_width = pyautogui.size().width
|
|
driver.maximize_window()
|
|
win_width = driver.get_window_size()["width"]
|
|
width_ratio = round(float(scr_width) / float(win_width), 2) + 0.01
|
|
if width_ratio < 0.45 or width_ratio > 2.55:
|
|
width_ratio = 1.01
|
|
sb_config._saved_width_ratio = width_ratio
|
|
driver.minimize_window()
|
|
driver.set_window_rect(win_x, win_y, width, height)
|
|
elif IS_WINDOWS and __is_cdp_swap_needed(driver):
|
|
window_rect = driver.cdp.get_window_rect()
|
|
width = window_rect["width"]
|
|
height = window_rect["height"]
|
|
win_x = window_rect["x"]
|
|
win_y = window_rect["y"]
|
|
scr_width = pyautogui.size().width
|
|
driver.cdp.maximize()
|
|
win_width = driver.cdp.get_window_size()["width"]
|
|
width_ratio = round(float(scr_width) / float(win_width), 2) + 0.01
|
|
if width_ratio < 0.45 or width_ratio > 2.55:
|
|
width_ratio = 1.01
|
|
sb_config._saved_width_ratio = width_ratio
|
|
driver.cdp.minimize()
|
|
driver.cdp.set_window_rect(win_x, win_y, width, height)
|
|
if IS_WINDOWS:
|
|
x = x * width_ratio
|
|
y = y * width_ratio
|
|
_uc_gui_click_x_y(driver, x, y, timeframe=timeframe, uc_lock=False)
|
|
return
|
|
with suppress(Exception):
|
|
page_actions.switch_to_window(
|
|
driver, driver.current_window_handle, 2, uc_lock=False
|
|
)
|
|
_uc_gui_click_x_y(driver, x, y, timeframe=timeframe, uc_lock=False)
|
|
|
|
|
|
def _on_a_cf_turnstile_page(driver):
|
|
source = driver.get_page_source()
|
|
if (
|
|
'data-callback="onCaptchaSuccess"' in source
|
|
or "/challenge-platform/scripts/" in source
|
|
or 'id="challenge-widget-' in source
|
|
or "cf-turnstile-" in source
|
|
):
|
|
return True
|
|
return False
|
|
|
|
|
|
def _on_a_g_recaptcha_page(driver):
|
|
source = driver.get_page_source()
|
|
if (
|
|
'id="recaptcha-token"' in source
|
|
or 'title="reCAPTCHA"' in source
|
|
):
|
|
return True
|
|
return False
|
|
|
|
|
|
def _uc_gui_click_captcha(
|
|
driver,
|
|
frame="iframe",
|
|
retry=False,
|
|
blind=False,
|
|
ctype=None,
|
|
):
|
|
cdp_mode_on_at_start = __is_cdp_swap_needed(driver)
|
|
_on_a_captcha_page = None
|
|
if ctype == "cf_t":
|
|
if not _on_a_cf_turnstile_page(driver):
|
|
return
|
|
else:
|
|
_on_a_captcha_page = _on_a_cf_turnstile_page
|
|
elif ctype == "g_rc":
|
|
if not _on_a_g_recaptcha_page(driver):
|
|
return
|
|
else:
|
|
_on_a_captcha_page = _on_a_g_recaptcha_page
|
|
else:
|
|
if _on_a_g_recaptcha_page(driver):
|
|
ctype = "g_rc"
|
|
_on_a_captcha_page = _on_a_g_recaptcha_page
|
|
elif _on_a_cf_turnstile_page(driver):
|
|
ctype = "cf_t"
|
|
_on_a_captcha_page = _on_a_cf_turnstile_page
|
|
else:
|
|
return
|
|
install_pyautogui_if_missing(driver)
|
|
import pyautogui
|
|
pyautogui = get_configured_pyautogui(pyautogui)
|
|
i_x = None
|
|
i_y = None
|
|
x = None
|
|
y = None
|
|
visible_iframe = True
|
|
gui_lock = fasteners.InterProcessLock(
|
|
constants.MultiBrowser.PYAUTOGUILOCK
|
|
)
|
|
with gui_lock: # Prevent issues with multiple processes
|
|
needs_switch = False
|
|
width_ratio = 1.0
|
|
is_in_frame = js_utils.is_in_frame(driver)
|
|
if is_in_frame and driver.is_element_present("#challenge-stage"):
|
|
driver.switch_to.parent_frame()
|
|
needs_switch = True
|
|
is_in_frame = js_utils.is_in_frame(driver)
|
|
if not is_in_frame:
|
|
# Make sure the window is on top
|
|
if __is_cdp_swap_needed(driver):
|
|
driver.cdp.bring_active_window_to_front()
|
|
else:
|
|
page_actions.switch_to_window(
|
|
driver, driver.current_window_handle, 2, uc_lock=False
|
|
)
|
|
if IS_WINDOWS and not __is_cdp_swap_needed(driver):
|
|
window_rect = driver.get_window_rect()
|
|
width = window_rect["width"]
|
|
height = window_rect["height"]
|
|
win_x = window_rect["x"]
|
|
win_y = window_rect["y"]
|
|
scr_width = pyautogui.size().width
|
|
driver.maximize_window()
|
|
win_width = driver.get_window_size()["width"]
|
|
width_ratio = round(float(scr_width) / float(win_width), 2) + 0.01
|
|
if width_ratio < 0.45 or width_ratio > 2.55:
|
|
width_ratio = 1.01
|
|
sb_config._saved_width_ratio = width_ratio
|
|
driver.minimize_window()
|
|
driver.set_window_rect(win_x, win_y, width, height)
|
|
elif IS_WINDOWS and __is_cdp_swap_needed(driver):
|
|
window_rect = driver.cdp.get_window_rect()
|
|
width = window_rect["width"]
|
|
height = window_rect["height"]
|
|
win_x = window_rect["x"]
|
|
win_y = window_rect["y"]
|
|
scr_width = pyautogui.size().width
|
|
driver.cdp.maximize()
|
|
win_width = driver.cdp.get_window_size()["width"]
|
|
width_ratio = round(float(scr_width) / float(win_width), 2) + 0.01
|
|
if width_ratio < 0.45 or width_ratio > 2.55:
|
|
width_ratio = 1.01
|
|
sb_config._saved_width_ratio = width_ratio
|
|
driver.cdp.minimize()
|
|
driver.cdp.set_window_rect(win_x, win_y, width, height)
|
|
if ctype == "cf_t":
|
|
if (
|
|
driver.is_element_present(".cf-turnstile-wrapper iframe")
|
|
or driver.is_element_present(
|
|
'[data-callback="onCaptchaSuccess"] iframe'
|
|
)
|
|
):
|
|
pass
|
|
else:
|
|
visible_iframe = False
|
|
if (
|
|
frame != "iframe"
|
|
and driver.is_element_present(
|
|
"%s .cf-turnstile-wrapper" % frame
|
|
)
|
|
):
|
|
frame = "%s .cf-turnstile-wrapper" % frame
|
|
elif (
|
|
frame != "iframe"
|
|
and driver.is_element_present(
|
|
'%s [name*="cf-turnstile"]' % frame
|
|
)
|
|
and driver.is_element_present("%s div" % frame)
|
|
):
|
|
frame = "%s div" % frame
|
|
elif (
|
|
driver.is_element_present('[name*="cf-turnstile-"]')
|
|
and driver.is_element_present("#challenge-form div > div")
|
|
):
|
|
frame = "#challenge-form div > div"
|
|
elif (
|
|
driver.is_element_present('[name*="cf-turnstile-"]')
|
|
and driver.is_element_present(
|
|
'[style="display: grid;"] div div'
|
|
)
|
|
):
|
|
frame = '[style="display: grid;"] div div'
|
|
elif (
|
|
driver.is_element_present('[name*="cf-turnstile-"]')
|
|
and driver.is_element_present("[class*=spacer] + div div")
|
|
):
|
|
frame = '[class*=spacer] + div div'
|
|
elif (
|
|
driver.is_element_present('[name*="cf-turnstile-"]')
|
|
and driver.is_element_present("div.spacer div")
|
|
):
|
|
frame = "div.spacer div"
|
|
elif (
|
|
driver.is_element_present('script[src*="challenges.c"]')
|
|
and driver.is_element_present(
|
|
'[data-testid*="challenge-"] div'
|
|
)
|
|
):
|
|
frame = '[data-testid*="challenge-"] div'
|
|
elif driver.is_element_present(
|
|
"form.turnstile div#turnstile-widget div:not([class])"
|
|
):
|
|
frame = "form.turnstile #turnstile-widget div:not([class])"
|
|
elif driver.is_element_present(
|
|
'form div:not([class]):has(input[name*="cf-turn"])'
|
|
):
|
|
frame = 'form div:not([class]):has(input[name*="cf-turn"])'
|
|
elif (
|
|
driver.is_element_present('[src*="/turnstile/"]')
|
|
and driver.is_element_present("form div:not(:has(*))")
|
|
):
|
|
frame = "form div:not(:has(*))"
|
|
elif (
|
|
driver.is_element_present('[src*="/turnstile/"]')
|
|
and driver.is_element_present(
|
|
"body > div#check > div:not([class])"
|
|
)
|
|
):
|
|
frame = "body > div#check > div:not([class])"
|
|
elif driver.is_element_present(".cf-turnstile-wrapper"):
|
|
frame = ".cf-turnstile-wrapper"
|
|
elif driver.is_element_present('[class="cf-turnstile"]'):
|
|
frame = '[class="cf-turnstile"]'
|
|
elif driver.is_element_present(
|
|
'[data-callback="onCaptchaSuccess"]'
|
|
):
|
|
frame = '[data-callback="onCaptchaSuccess"]'
|
|
else:
|
|
return
|
|
if (
|
|
driver.is_element_present("form")
|
|
and (
|
|
driver.is_element_present('form[class*="center"]')
|
|
or driver.is_element_present('form[class*="right"]')
|
|
or driver.is_element_present('form div[class*="center"]')
|
|
or driver.is_element_present('form div[class*="right"]')
|
|
)
|
|
):
|
|
script = (
|
|
"""var $elements = document.querySelectorAll(
|
|
'form[class], form div[class]');
|
|
var index = 0, length = $elements.length;
|
|
for(; index < length; index++){
|
|
the_class = $elements[index].getAttribute('class');
|
|
new_class = the_class.replaceAll('center', 'left');
|
|
new_class = new_class.replaceAll('right', 'left');
|
|
$elements[index].setAttribute('class', new_class);}"""
|
|
)
|
|
if __is_cdp_swap_needed(driver):
|
|
driver.cdp.evaluate(script)
|
|
else:
|
|
driver.execute_script(script)
|
|
elif (
|
|
driver.is_element_present("form")
|
|
and (
|
|
driver.is_element_present('form div[style*="center"]')
|
|
or driver.is_element_present('form div[style*="right"]')
|
|
)
|
|
):
|
|
script = (
|
|
"""var $elements = document.querySelectorAll(
|
|
'form[style], form div[style]');
|
|
var index = 0, length = $elements.length;
|
|
for(; index < length; index++){
|
|
the_style = $elements[index].getAttribute('style');
|
|
new_style = the_style.replaceAll('center', 'left');
|
|
new_style = new_style.replaceAll('right', 'left');
|
|
$elements[index].setAttribute('style', new_style);}"""
|
|
)
|
|
if __is_cdp_swap_needed(driver):
|
|
driver.cdp.evaluate(script)
|
|
else:
|
|
driver.execute_script(script)
|
|
elif (
|
|
driver.is_element_present("form")
|
|
and driver.is_element_present(
|
|
'form [id*="turnstile"] > div:not([class])'
|
|
)
|
|
):
|
|
script = (
|
|
"""var $elements = document.querySelectorAll(
|
|
'form [id*="turnstile"]');
|
|
var index = 0, length = $elements.length;
|
|
for(; index < length; index++){
|
|
$elements[index].setAttribute('align', 'left');}"""
|
|
)
|
|
if __is_cdp_swap_needed(driver):
|
|
driver.cdp.evaluate(script)
|
|
else:
|
|
driver.execute_script(script)
|
|
if not is_in_frame or needs_switch:
|
|
# Currently not in frame (or nested frame outside CF one)
|
|
try:
|
|
i_x, i_y = get_gui_element_position(driver, frame)
|
|
if visible_iframe:
|
|
driver.switch_to_frame(frame)
|
|
except Exception:
|
|
if visible_iframe:
|
|
if driver.is_element_present("iframe"):
|
|
i_x, i_y = get_gui_element_position(driver, "iframe")
|
|
if driver.is_connected():
|
|
driver.switch_to_frame("iframe")
|
|
else:
|
|
return
|
|
if not i_x or not i_y:
|
|
return
|
|
try:
|
|
if ctype == "g_rc" and not driver.is_connected():
|
|
x = (i_x + 29) * width_ratio
|
|
y = (i_y + 35) * width_ratio
|
|
elif visible_iframe:
|
|
selector = "span"
|
|
if ctype == "g_rc":
|
|
selector = "span.recaptcha-checkbox"
|
|
if not driver.is_connected():
|
|
selector = "iframe"
|
|
element = driver.wait_for_element_present(
|
|
selector, timeout=2.5
|
|
)
|
|
x = i_x + element.rect["x"] + (element.rect["width"] / 2.0)
|
|
x += 0.5
|
|
y = i_y + element.rect["y"] + (element.rect["height"] / 2.0)
|
|
y += 0.5
|
|
else:
|
|
x = (i_x + 32) * width_ratio
|
|
y = (i_y + 32) * width_ratio
|
|
if driver.is_connected():
|
|
driver.switch_to.default_content()
|
|
except Exception:
|
|
if driver.is_connected():
|
|
try:
|
|
driver.switch_to.default_content()
|
|
except Exception:
|
|
return
|
|
if x and y:
|
|
sb_config._saved_cf_x_y = (x, y)
|
|
if not __is_cdp_swap_needed(driver):
|
|
if driver.is_element_present(".footer .clearfix .ray-id"):
|
|
driver.uc_open_with_disconnect(
|
|
driver.get_current_url(), 3.8
|
|
)
|
|
else:
|
|
driver.disconnect()
|
|
with suppress(Exception):
|
|
_uc_gui_click_x_y(driver, x, y, timeframe=0.32)
|
|
if __is_cdp_swap_needed(driver):
|
|
time.sleep(float(constants.UC.RECONNECT_TIME) / 2.0)
|
|
return
|
|
reconnect_time = (float(constants.UC.RECONNECT_TIME) / 2.0) + 0.6
|
|
if IS_LINUX:
|
|
reconnect_time = constants.UC.RECONNECT_TIME + 0.2
|
|
if not x or not y:
|
|
reconnect_time = 1 # Make it quick (it already failed)
|
|
driver.reconnect(reconnect_time)
|
|
caught = False
|
|
if (
|
|
driver.is_element_present(".footer .clearfix .ray-id")
|
|
and not driver.is_element_visible("#challenge-success-text")
|
|
):
|
|
blind = True
|
|
caught = True
|
|
if blind:
|
|
retry = True
|
|
if retry and x and y and (caught or _on_a_captcha_page(driver)):
|
|
with gui_lock: # Prevent issues with multiple processes
|
|
# Make sure the window is on top
|
|
if __is_cdp_swap_needed(driver):
|
|
driver.cdp.bring_active_window_to_front()
|
|
else:
|
|
page_actions.switch_to_window(
|
|
driver, driver.current_window_handle, 2, uc_lock=False
|
|
)
|
|
if driver.is_element_present("iframe"):
|
|
try:
|
|
driver.switch_to_frame(frame)
|
|
except Exception:
|
|
try:
|
|
driver.switch_to_frame("iframe")
|
|
except Exception:
|
|
return
|
|
checkbox_success = None
|
|
if ctype == "cf_t":
|
|
checkbox_success = "#success-icon"
|
|
elif ctype == "g_rc":
|
|
checkbox_success = "span.recaptcha-checkbox-checked"
|
|
else:
|
|
return # If this line is reached, ctype wasn't set
|
|
if driver.is_element_visible("#success-icon"):
|
|
driver.switch_to.parent_frame(checkbox_success)
|
|
return
|
|
if blind:
|
|
driver.uc_open_with_disconnect(driver.get_current_url(), 3.8)
|
|
if __is_cdp_swap_needed(driver) and _on_a_captcha_page(driver):
|
|
_uc_gui_click_x_y(driver, x, y, timeframe=0.32)
|
|
else:
|
|
time.sleep(0.1)
|
|
else:
|
|
driver.uc_open_with_reconnect(driver.get_current_url(), 3.8)
|
|
if _on_a_captcha_page(driver):
|
|
driver.disconnect()
|
|
_uc_gui_click_x_y(driver, x, y, timeframe=0.32)
|
|
if not cdp_mode_on_at_start:
|
|
driver.reconnect(reconnect_time)
|
|
|
|
|
|
def uc_gui_click_captcha(driver, frame="iframe", retry=False, blind=False):
|
|
_uc_gui_click_captcha(
|
|
driver,
|
|
frame=frame,
|
|
retry=retry,
|
|
blind=blind,
|
|
ctype=None,
|
|
)
|
|
|
|
|
|
def uc_gui_click_rc(driver, frame="iframe", retry=False, blind=False):
|
|
_uc_gui_click_captcha(
|
|
driver,
|
|
frame=frame,
|
|
retry=retry,
|
|
blind=blind,
|
|
ctype="g_rc",
|
|
)
|
|
|
|
|
|
def uc_gui_click_cf(driver, frame="iframe", retry=False, blind=False):
|
|
_uc_gui_click_captcha(
|
|
driver,
|
|
frame=frame,
|
|
retry=retry,
|
|
blind=blind,
|
|
ctype="cf_t",
|
|
)
|
|
|
|
|
|
def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
|
|
if ctype == "cf_t":
|
|
if not _on_a_cf_turnstile_page(driver):
|
|
return
|
|
elif ctype == "g_rc":
|
|
if not _on_a_g_recaptcha_page(driver):
|
|
return
|
|
else:
|
|
if _on_a_g_recaptcha_page(driver):
|
|
ctype = "g_rc"
|
|
elif _on_a_cf_turnstile_page(driver):
|
|
ctype = "cf_t"
|
|
else:
|
|
return
|
|
if not driver.is_connected() and not __is_cdp_swap_needed(driver):
|
|
driver.connect()
|
|
time.sleep(2)
|
|
install_pyautogui_if_missing(driver)
|
|
import pyautogui
|
|
pyautogui = get_configured_pyautogui(pyautogui)
|
|
visible_iframe = True
|
|
gui_lock = fasteners.InterProcessLock(
|
|
constants.MultiBrowser.PYAUTOGUILOCK
|
|
)
|
|
with gui_lock: # Prevent issues with multiple processes
|
|
needs_switch = False
|
|
if not __is_cdp_swap_needed(driver):
|
|
is_in_frame = js_utils.is_in_frame(driver)
|
|
else:
|
|
is_in_frame = False
|
|
selector = "#challenge-stage"
|
|
if ctype == "g_rc":
|
|
selector = "#recaptcha-token"
|
|
if is_in_frame and driver.is_element_present(selector):
|
|
driver.switch_to.parent_frame()
|
|
needs_switch = True
|
|
is_in_frame = js_utils.is_in_frame(driver)
|
|
if not is_in_frame and not __is_cdp_swap_needed(driver):
|
|
# Make sure the window is on top
|
|
page_actions.switch_to_window(
|
|
driver, driver.current_window_handle, 2, uc_lock=False
|
|
)
|
|
if IS_WINDOWS and hasattr(pyautogui, "getActiveWindowTitle"):
|
|
py_a_g_title = pyautogui.getActiveWindowTitle() or ""
|
|
window_title = driver.get_title()
|
|
if not py_a_g_title.startswith(window_title):
|
|
window_rect = driver.get_window_rect()
|
|
width = window_rect["width"]
|
|
height = window_rect["height"]
|
|
win_x = window_rect["x"]
|
|
win_y = window_rect["y"]
|
|
driver.minimize_window()
|
|
driver.set_window_rect(win_x, win_y, width, height)
|
|
time.sleep(0.33)
|
|
tab_up_first = False
|
|
special_form = False
|
|
if ctype == "cf_t":
|
|
if (
|
|
driver.is_element_present(".cf-turnstile-wrapper iframe")
|
|
or driver.is_element_present(
|
|
'[data-callback="onCaptchaSuccess"] iframe'
|
|
)
|
|
):
|
|
pass
|
|
else:
|
|
visible_iframe = False
|
|
if driver.is_element_present(".cf-turnstile-wrapper"):
|
|
frame = ".cf-turnstile-wrapper"
|
|
elif driver.is_element_present(
|
|
'[data-callback="onCaptchaSuccess"]'
|
|
):
|
|
frame = '[data-callback="onCaptchaSuccess"]'
|
|
elif (
|
|
driver.is_element_present('[name*="cf-turnstile-"]')
|
|
and driver.is_element_present("div.spacer div")
|
|
):
|
|
frame = "div.spacer div"
|
|
elif (
|
|
driver.is_element_present('script[src*="challenges.c"]')
|
|
and driver.is_element_present(
|
|
'[data-testid*="challenge-"] div'
|
|
)
|
|
):
|
|
frame = '[data-testid*="challenge-"] div'
|
|
elif driver.is_element_present(
|
|
'form div:not([class]):has(input[name*="cf-turn"])'
|
|
):
|
|
frame = 'form div:not([class]):has(input[name*="cf-turn"])'
|
|
tab_up_first = True
|
|
special_form = True
|
|
elif (
|
|
driver.is_element_present('[src*="/turnstile/"]')
|
|
and driver.is_element_present("form div:not(:has(*))")
|
|
):
|
|
frame = "form div:not(:has(*))"
|
|
tab_up_first = True
|
|
elif (
|
|
driver.is_element_present('[src*="/turnstile/"]')
|
|
and driver.is_element_present(
|
|
"body > div#check > div:not([class])"
|
|
)
|
|
):
|
|
frame = "body > div#check > div:not([class])"
|
|
elif driver.is_element_present(".cf-turnstile-wrapper"):
|
|
frame = ".cf-turnstile-wrapper"
|
|
elif driver.is_element_present('[class="cf-turnstile"]'):
|
|
frame = '[class="cf-turnstile"]'
|
|
else:
|
|
return
|
|
else:
|
|
if (
|
|
driver.is_element_present('iframe[title="reCAPTCHA"]')
|
|
and frame == "iframe"
|
|
):
|
|
frame = 'iframe[title="reCAPTCHA"]'
|
|
if not __is_cdp_swap_needed(driver):
|
|
if not is_in_frame or needs_switch:
|
|
# Currently not in frame (or nested frame outside CF one)
|
|
try:
|
|
if visible_iframe or ctype == "g_rc":
|
|
driver.switch_to_frame(frame)
|
|
except Exception:
|
|
if visible_iframe or ctype == "g_rc":
|
|
if driver.is_element_present("iframe"):
|
|
driver.switch_to_frame("iframe")
|
|
else:
|
|
return
|
|
try:
|
|
selector = "div.cf-turnstile"
|
|
if ctype == "g_rc":
|
|
selector = "span#recaptcha-anchor"
|
|
found_checkbox = False
|
|
if tab_up_first:
|
|
for i in range(10):
|
|
pyautogui.hotkey("shift", "tab")
|
|
time.sleep(0.027)
|
|
if ctype == "g_rc":
|
|
if js_utils.get_active_element_css(driver) == "body":
|
|
break
|
|
tab_count = 0
|
|
for i in range(34):
|
|
pyautogui.press("\t")
|
|
tab_count += 1
|
|
time.sleep(0.027)
|
|
active_element_css = js_utils.get_active_element_css(driver)
|
|
if (
|
|
active_element_css.startswith(selector)
|
|
or active_element_css.endswith(" > div" * 2)
|
|
or (special_form and active_element_css.endswith(" div"))
|
|
or (ctype == "g_rc" and "frame[name" in active_element_css)
|
|
):
|
|
found_checkbox = True
|
|
sb_config._saved_cf_tab_count = tab_count
|
|
break
|
|
time.sleep(0.02)
|
|
if not found_checkbox:
|
|
return
|
|
except Exception:
|
|
try:
|
|
driver.switch_to.default_content()
|
|
except Exception:
|
|
return
|
|
if (
|
|
(
|
|
driver.is_element_present(".footer .clearfix .ray-id")
|
|
or driver.is_element_present("script[data-cf-beacon]")
|
|
)
|
|
and hasattr(sb_config, "_saved_cf_tab_count")
|
|
and sb_config._saved_cf_tab_count
|
|
and not __is_cdp_swap_needed(driver)
|
|
):
|
|
driver.uc_open_with_disconnect(driver.current_url, 3.8)
|
|
with suppress(Exception):
|
|
if "--debug" in sys.argv:
|
|
if sb_config._saved_cf_tab_count == 1:
|
|
print(' <DEBUG> pyautogui.press("\\t")')
|
|
else:
|
|
print(
|
|
' <DEBUG> pyautogui.press("\\t") * %s'
|
|
% sb_config._saved_cf_tab_count
|
|
)
|
|
for i in range(sb_config._saved_cf_tab_count):
|
|
pyautogui.press("\t")
|
|
time.sleep(0.027)
|
|
if "--debug" in sys.argv:
|
|
print(' <DEBUG> pyautogui.press(" ")')
|
|
pyautogui.press(" ")
|
|
else:
|
|
driver.disconnect()
|
|
pyautogui.press(" ")
|
|
reconnect_time = (float(constants.UC.RECONNECT_TIME) / 2.0) + 0.6
|
|
if IS_LINUX:
|
|
reconnect_time = constants.UC.RECONNECT_TIME + 0.2
|
|
driver.reconnect(reconnect_time)
|
|
|
|
|
|
def _uc_gui_handle_captcha(driver, frame="iframe", ctype=None):
|
|
_uc_gui_handle_captcha_(driver, frame=frame, ctype=ctype)
|
|
if (
|
|
driver.is_element_present(".footer .clearfix .ray-id")
|
|
and not driver.is_element_visible("#challenge-success-text")
|
|
):
|
|
driver.uc_open_with_reconnect(driver.current_url, 3.8)
|
|
_uc_gui_handle_captcha_(driver, frame=frame, ctype=ctype)
|
|
|
|
|
|
def uc_gui_handle_captcha(driver, frame="iframe"):
|
|
_uc_gui_handle_captcha(driver, frame=frame, ctype=None)
|
|
|
|
|
|
def uc_gui_handle_cf(driver, frame="iframe"):
|
|
_uc_gui_handle_captcha(driver, frame=frame, ctype="cf_t")
|
|
|
|
|
|
def uc_gui_handle_rc(driver, frame="iframe"):
|
|
_uc_gui_handle_captcha(driver, frame=frame, ctype="g_rc")
|
|
|
|
|
|
def uc_switch_to_frame(driver, frame="iframe", reconnect_time=None):
|
|
from selenium.webdriver.remote.webelement import WebElement
|
|
if isinstance(frame, WebElement):
|
|
if not reconnect_time:
|
|
driver.reconnect(0.1)
|
|
else:
|
|
driver.reconnect(reconnect_time)
|
|
driver.switch_to.frame(frame)
|
|
else:
|
|
iframe = driver.locator(frame)
|
|
if not reconnect_time:
|
|
driver.reconnect(0.1)
|
|
else:
|
|
driver.reconnect(reconnect_time)
|
|
driver.switch_to.frame(iframe)
|
|
|
|
|
|
def edgedriver_on_path():
|
|
return os.path.exists(LOCAL_EDGEDRIVER)
|
|
|
|
|
|
def geckodriver_on_path():
|
|
paths = os.environ["PATH"].split(os.pathsep)
|
|
for path in paths:
|
|
if (not IS_WINDOWS) and os.path.exists(path + "/geckodriver"):
|
|
return True
|
|
elif IS_WINDOWS and os.path.exists(path + "/geckodriver.exe"):
|
|
return True
|
|
return False
|
|
|
|
|
|
def iedriver_on_path():
|
|
paths = os.environ["PATH"].split(os.pathsep)
|
|
for path in paths:
|
|
if os.path.exists(path + "/IEDriverServer.exe"):
|
|
return True
|
|
return False
|
|
|
|
|
|
def headless_iedriver_on_path():
|
|
return os.path.exists(LOCAL_HEADLESS_IEDRIVER)
|
|
|
|
|
|
def get_valid_binary_names_for_browser(browser):
|
|
if browser == constants.Browser.GOOGLE_CHROME:
|
|
if IS_LINUX:
|
|
return constants.ValidBinaries.valid_chrome_binaries_on_linux
|
|
elif IS_MAC:
|
|
return constants.ValidBinaries.valid_chrome_binaries_on_macos
|
|
elif IS_WINDOWS:
|
|
return constants.ValidBinaries.valid_chrome_binaries_on_windows
|
|
else:
|
|
raise Exception("Could not determine OS, or unsupported!")
|
|
elif browser == constants.Browser.EDGE:
|
|
if IS_LINUX:
|
|
return constants.ValidBinaries.valid_edge_binaries_on_linux
|
|
elif IS_MAC:
|
|
return constants.ValidBinaries.valid_edge_binaries_on_macos
|
|
elif IS_WINDOWS:
|
|
return constants.ValidBinaries.valid_edge_binaries_on_windows
|
|
else:
|
|
raise Exception("Could not determine OS, or unsupported!")
|
|
else:
|
|
raise Exception("Invalid combination for OS browser binaries!")
|
|
|
|
|
|
def _repair_chromedriver(chrome_options, headless_options, mcv=None):
|
|
if mcv:
|
|
subprocess.check_call(
|
|
"sbase get chromedriver %s" % mcv, shell=True
|
|
)
|
|
return
|
|
driver = None
|
|
subprocess.check_call(
|
|
"sbase get chromedriver 72.0.3626.69", shell=True
|
|
)
|
|
try:
|
|
service = ChromeService(executable_path=LOCAL_CHROMEDRIVER)
|
|
driver = webdriver.Chrome(
|
|
service=service,
|
|
options=headless_options,
|
|
)
|
|
except Exception:
|
|
subprocess.check_call(
|
|
"sbase get chromedriver latest-1", shell=True
|
|
)
|
|
return
|
|
chrome_version = None
|
|
if "version" in driver.capabilities:
|
|
chrome_version = driver.capabilities["version"]
|
|
else:
|
|
chrome_version = driver.capabilities["browserVersion"]
|
|
major_chrome_ver = chrome_version.split(".")[0]
|
|
chrome_dict = driver.capabilities["chrome"]
|
|
driver.quit()
|
|
chromedriver_ver = chrome_dict["chromedriverVersion"]
|
|
chromedriver_ver = chromedriver_ver.split(" ")[0]
|
|
major_chromedriver_ver = chromedriver_ver.split(".")[0]
|
|
if (
|
|
major_chromedriver_ver != major_chrome_ver
|
|
and int(major_chrome_ver) >= 73
|
|
):
|
|
subprocess.check_call(
|
|
"sbase get chromedriver %s" % major_chrome_ver, shell=True
|
|
)
|
|
return
|
|
|
|
|
|
def _repair_edgedriver(edge_version):
|
|
log_d(
|
|
"\nWarning: msedgedriver version doesn't match Edge version!"
|
|
"\nAttempting to install a matching version of msedgedriver:"
|
|
)
|
|
subprocess.check_call(
|
|
"sbase get edgedriver %s" % edge_version, shell=True
|
|
)
|
|
return
|
|
|
|
|
|
def _mark_driver_repaired():
|
|
import codecs
|
|
|
|
abs_path = os.path.abspath(".")
|
|
driver_repaired_lock = constants.MultiBrowser.DRIVER_REPAIRED
|
|
file_path = os.path.join(abs_path, driver_repaired_lock)
|
|
if not os.path.exists(DOWNLOADS_FOLDER):
|
|
os.makedirs(DOWNLOADS_FOLDER)
|
|
out_file = codecs.open(file_path, "w+", encoding="utf-8")
|
|
out_file.writelines("")
|
|
out_file.close()
|
|
|
|
|
|
def _was_driver_repaired():
|
|
abs_path = os.path.abspath(".")
|
|
driver_repaired_lock = constants.MultiBrowser.DRIVER_REPAIRED
|
|
file_path = os.path.join(abs_path, driver_repaired_lock)
|
|
return os.path.exists(file_path)
|
|
|
|
|
|
def _set_proxy_filenames():
|
|
DOWNLOADS_DIR = constants.Files.DOWNLOADS_FOLDER
|
|
for num in range(1000):
|
|
PROXY_ZIP_PATH = os.path.join(DOWNLOADS_DIR, "proxy_%s.zip" % num)
|
|
PROXY_DIR_PATH = os.path.join(DOWNLOADS_DIR, "proxy_ext_dir_%s" % num)
|
|
if os.path.exists(PROXY_ZIP_PATH) or os.path.exists(PROXY_DIR_PATH):
|
|
continue
|
|
proxy_helper.PROXY_ZIP_PATH = PROXY_ZIP_PATH
|
|
proxy_helper.PROXY_DIR_PATH = PROXY_DIR_PATH
|
|
return
|
|
# Exceeded upper bound. Use Defaults:
|
|
PROXY_ZIP_PATH = os.path.join(DOWNLOADS_DIR, "proxy.zip")
|
|
PROXY_DIR_PATH = os.path.join(DOWNLOADS_DIR, "proxy_ext_dir")
|
|
proxy_helper.PROXY_ZIP_PATH = PROXY_ZIP_PATH
|
|
proxy_helper.PROXY_DIR_PATH = PROXY_DIR_PATH
|
|
|
|
|
|
def _add_chrome_proxy_extension(
|
|
chrome_options,
|
|
proxy_string,
|
|
proxy_user,
|
|
proxy_pass,
|
|
proxy_bypass_list=None,
|
|
zip_it=True,
|
|
multi_proxy=False,
|
|
):
|
|
"""Implementation of https://stackoverflow.com/a/35293284/7058266
|
|
for https://stackoverflow.com/q/12848327/7058266
|
|
(Run Selenium on a proxy server that requires authentication.)"""
|
|
args = " ".join(sys.argv)
|
|
bypass_list = proxy_bypass_list
|
|
if (
|
|
not ("-n" in sys.argv or " -n=" in args or args == "-c")
|
|
and not multi_proxy
|
|
):
|
|
# Single-threaded
|
|
if zip_it:
|
|
proxy_zip_lock = fasteners.InterProcessLock(PROXY_ZIP_LOCK)
|
|
with proxy_zip_lock:
|
|
proxy_helper.create_proxy_ext(
|
|
proxy_string, proxy_user, proxy_pass, bypass_list
|
|
)
|
|
proxy_zip = proxy_helper.PROXY_ZIP_PATH
|
|
chrome_options.add_extension(proxy_zip)
|
|
else:
|
|
proxy_dir_lock = fasteners.InterProcessLock(PROXY_DIR_LOCK)
|
|
with proxy_dir_lock:
|
|
proxy_helper.create_proxy_ext(
|
|
proxy_string,
|
|
proxy_user,
|
|
proxy_pass,
|
|
bypass_list,
|
|
zip_it=False,
|
|
)
|
|
proxy_dir_path = proxy_helper.PROXY_DIR_PATH
|
|
chrome_options = add_chrome_ext_dir(
|
|
chrome_options, proxy_dir_path
|
|
)
|
|
else:
|
|
# Multi-threaded
|
|
if zip_it:
|
|
proxy_zip_lock = fasteners.InterProcessLock(PROXY_ZIP_LOCK)
|
|
with proxy_zip_lock:
|
|
with suppress(Exception):
|
|
shared_utils.make_writable(PROXY_ZIP_LOCK)
|
|
if multi_proxy:
|
|
_set_proxy_filenames()
|
|
if not os.path.exists(proxy_helper.PROXY_ZIP_PATH):
|
|
proxy_helper.create_proxy_ext(
|
|
proxy_string, proxy_user, proxy_pass, bypass_list
|
|
)
|
|
proxy_zip = proxy_helper.PROXY_ZIP_PATH
|
|
chrome_options.add_extension(proxy_zip)
|
|
else:
|
|
proxy_dir_lock = fasteners.InterProcessLock(PROXY_DIR_LOCK)
|
|
with proxy_dir_lock:
|
|
with suppress(Exception):
|
|
shared_utils.make_writable(PROXY_DIR_LOCK)
|
|
if multi_proxy:
|
|
_set_proxy_filenames()
|
|
if not os.path.exists(proxy_helper.PROXY_DIR_PATH):
|
|
proxy_helper.create_proxy_ext(
|
|
proxy_string,
|
|
proxy_user,
|
|
proxy_pass,
|
|
bypass_list,
|
|
zip_it=False,
|
|
)
|
|
chrome_options = add_chrome_ext_dir(
|
|
chrome_options, proxy_helper.PROXY_DIR_PATH
|
|
)
|
|
return chrome_options
|
|
|
|
|
|
def is_using_uc(undetectable, browser_name):
|
|
if undetectable and browser_name == constants.Browser.GOOGLE_CHROME:
|
|
return True
|
|
return False
|
|
|
|
|
|
def _unzip_to_new_folder(zip_file, folder):
|
|
proxy_dir_lock = fasteners.InterProcessLock(PROXY_DIR_LOCK)
|
|
with proxy_dir_lock:
|
|
with suppress(Exception):
|
|
shared_utils.make_writable(PROXY_DIR_LOCK)
|
|
if not os.path.exists(folder):
|
|
import zipfile
|
|
zip_ref = zipfile.ZipFile(zip_file, "r")
|
|
os.makedirs(folder)
|
|
zip_ref.extractall(folder)
|
|
zip_ref.close()
|
|
|
|
|
|
def add_chrome_ext_dir(chrome_options, dir_path):
|
|
option_exists = False
|
|
for arg in chrome_options.arguments:
|
|
if arg.startswith("--load-extension="):
|
|
option_exists = True
|
|
chrome_options.arguments.remove(arg)
|
|
chrome_options.add_argument(
|
|
"%s,%s" % (arg, os.path.realpath(dir_path))
|
|
)
|
|
if not option_exists:
|
|
chrome_options.add_argument(
|
|
"--load-extension=%s" % os.path.realpath(dir_path)
|
|
)
|
|
return chrome_options
|
|
|
|
|
|
def _add_chrome_disable_csp_extension(chrome_options):
|
|
"""Disable Chrome's Content-Security-Policy with a browser extension.
|
|
See https://github.com/PhilGrayson/chrome-csp-disable for details."""
|
|
chrome_options.add_extension(DISABLE_CSP_ZIP_PATH)
|
|
return chrome_options
|
|
|
|
|
|
def _add_chrome_ad_block_extension(chrome_options):
|
|
"""Block Ads on Chromium Browsers with a browser extension.
|
|
See https://github.com/slingamn/simpleblock for details."""
|
|
chrome_options.add_extension(AD_BLOCK_ZIP_PATH)
|
|
return chrome_options
|
|
|
|
|
|
def _add_chrome_recorder_extension(chrome_options):
|
|
"""The SeleniumBase Recorder Chrome/Edge extension.
|
|
https://seleniumbase.io/help_docs/recorder_mode/"""
|
|
chrome_options.add_extension(RECORDER_ZIP_PATH)
|
|
return chrome_options
|
|
|
|
|
|
def _set_chrome_options(
|
|
browser_name,
|
|
downloads_path,
|
|
headless,
|
|
locale_code,
|
|
proxy_string,
|
|
proxy_auth,
|
|
proxy_user,
|
|
proxy_pass,
|
|
proxy_bypass_list,
|
|
proxy_pac_url,
|
|
multi_proxy,
|
|
user_agent,
|
|
recorder_ext,
|
|
disable_cookies,
|
|
disable_js,
|
|
disable_csp,
|
|
enable_ws,
|
|
enable_sync,
|
|
use_auto_ext,
|
|
undetectable,
|
|
uc_cdp_events,
|
|
uc_subprocess,
|
|
log_cdp_events,
|
|
no_sandbox,
|
|
disable_gpu,
|
|
headless1,
|
|
headless2,
|
|
incognito,
|
|
guest_mode,
|
|
dark_mode,
|
|
devtools,
|
|
remote_debug,
|
|
enable_3d_apis,
|
|
swiftshader,
|
|
ad_block_on,
|
|
host_resolver_rules,
|
|
block_images,
|
|
do_not_track,
|
|
chromium_arg,
|
|
user_data_dir,
|
|
extension_zip,
|
|
extension_dir,
|
|
disable_features,
|
|
binary_location,
|
|
driver_version,
|
|
page_load_strategy,
|
|
use_wire,
|
|
external_pdf,
|
|
servername,
|
|
mobile_emulator,
|
|
device_width,
|
|
device_height,
|
|
device_pixel_ratio,
|
|
):
|
|
chrome_options = webdriver.ChromeOptions()
|
|
if is_using_uc(undetectable, browser_name):
|
|
from seleniumbase import undetected
|
|
chrome_options = undetected.ChromeOptions()
|
|
elif browser_name == constants.Browser.EDGE:
|
|
chrome_options = webdriver.edge.options.Options()
|
|
prefs = {}
|
|
prefs["download.default_directory"] = downloads_path
|
|
prefs["download.directory_upgrade"] = True
|
|
prefs["download.prompt_for_download"] = False
|
|
prefs["download_bubble.partial_view_enabled"] = False
|
|
prefs["credentials_enable_service"] = False
|
|
prefs["local_discovery.notifications_enabled"] = False
|
|
prefs["safebrowsing.enabled"] = False # Prevent PW "data breach" pop-ups
|
|
prefs["safebrowsing.disable_download_protection"] = True
|
|
prefs["omnibox-max-zero-suggest-matches"] = 0
|
|
prefs["omnibox-use-existing-autocomplete-client"] = 0
|
|
prefs["omnibox-trending-zero-prefix-suggestions-on-ntp"] = 0
|
|
prefs["omnibox-local-history-zero-suggest-beyond-ntp"] = 0
|
|
prefs["omnibox-on-focus-suggestions-contextual-web"] = 0
|
|
prefs["omnibox-on-focus-suggestions-srp"] = 0
|
|
prefs["omnibox-zero-suggest-prefetching"] = 0
|
|
prefs["omnibox-zero-suggest-prefetching-on-srp"] = 0
|
|
prefs["omnibox-zero-suggest-prefetching-on-web"] = 0
|
|
prefs["omnibox-zero-suggest-in-memory-caching"] = 0
|
|
prefs["content_settings.exceptions.automatic_downloads.*.setting"] = 1
|
|
prefs["default_content_setting_values.notifications"] = 0
|
|
prefs["default_content_settings.popups"] = 0
|
|
prefs["managed_default_content_settings.popups"] = 0
|
|
prefs["profile.password_manager_enabled"] = False
|
|
prefs["profile.password_manager_leak_detection"] = False
|
|
prefs["profile.default_content_setting_values.notifications"] = 2
|
|
prefs["profile.default_content_settings.popups"] = 0
|
|
prefs["profile.managed_default_content_settings.popups"] = 0
|
|
prefs["profile.default_content_setting_values.automatic_downloads"] = 1
|
|
if locale_code:
|
|
prefs["intl.accept_languages"] = locale_code
|
|
if block_images:
|
|
prefs["profile.managed_default_content_settings.images"] = 2
|
|
if disable_cookies:
|
|
prefs["profile.default_content_setting_values.cookies"] = 2
|
|
if disable_js:
|
|
prefs["profile.managed_default_content_settings.javascript"] = 2
|
|
if do_not_track:
|
|
prefs["enable_do_not_track"] = True
|
|
if external_pdf:
|
|
prefs["plugins.always_open_pdf_externally"] = True
|
|
if proxy_string or proxy_pac_url:
|
|
# Implementation of https://stackoverflow.com/q/65705775/7058266
|
|
prefs["webrtc.ip_handling_policy"] = "disable_non_proxied_udp"
|
|
prefs["webrtc.multiple_routes_enabled"] = False
|
|
prefs["webrtc.nonproxied_udp_enabled"] = False
|
|
chrome_options.add_experimental_option("prefs", prefs)
|
|
if enable_sync:
|
|
chrome_options.add_experimental_option(
|
|
"excludeSwitches",
|
|
["enable-automation", "enable-logging", "disable-sync"],
|
|
)
|
|
chrome_options.add_argument("--enable-sync")
|
|
else:
|
|
chrome_options.add_experimental_option(
|
|
"excludeSwitches",
|
|
["enable-automation", "enable-logging", "enable-blink-features"],
|
|
)
|
|
if log_cdp_events:
|
|
chrome_options.set_capability(
|
|
"goog:loggingPrefs", {"performance": "ALL", "browser": "ALL"}
|
|
)
|
|
if host_resolver_rules:
|
|
chrome_options.add_argument(
|
|
"--host-resolver-rules=%s" % host_resolver_rules
|
|
)
|
|
if mobile_emulator and not is_using_uc(undetectable, browser_name):
|
|
emulator_settings = {}
|
|
device_metrics = {}
|
|
if (
|
|
isinstance(device_width, int)
|
|
and isinstance(device_height, int)
|
|
and isinstance(device_pixel_ratio, (int, float))
|
|
):
|
|
device_metrics["width"] = device_width
|
|
device_metrics["height"] = device_height
|
|
device_metrics["pixelRatio"] = device_pixel_ratio
|
|
else:
|
|
device_metrics["width"] = constants.Mobile.WIDTH
|
|
device_metrics["height"] = constants.Mobile.HEIGHT
|
|
device_metrics["pixelRatio"] = constants.Mobile.RATIO
|
|
emulator_settings["deviceMetrics"] = device_metrics
|
|
if user_agent:
|
|
emulator_settings["userAgent"] = user_agent
|
|
chrome_options.add_experimental_option(
|
|
"mobileEmulation", emulator_settings
|
|
)
|
|
# Handle Window Position
|
|
if (headless or headless2) and IS_WINDOWS:
|
|
# https://stackoverflow.com/a/78999088/7058266
|
|
chrome_options.add_argument("--window-position=-2400,-2400")
|
|
else:
|
|
if (
|
|
hasattr(settings, "WINDOW_START_X")
|
|
and isinstance(settings.WINDOW_START_X, int)
|
|
and hasattr(settings, "WINDOW_START_Y")
|
|
and isinstance(settings.WINDOW_START_Y, int)
|
|
):
|
|
chrome_options.add_argument(
|
|
"--window-position=%s,%s" % (
|
|
settings.WINDOW_START_X, settings.WINDOW_START_Y
|
|
)
|
|
)
|
|
# Handle Window Size
|
|
if headless or headless2:
|
|
if (
|
|
hasattr(settings, "HEADLESS_START_WIDTH")
|
|
and isinstance(settings.HEADLESS_START_WIDTH, int)
|
|
and hasattr(settings, "HEADLESS_START_HEIGHT")
|
|
and isinstance(settings.HEADLESS_START_HEIGHT, int)
|
|
):
|
|
chrome_options.add_argument(
|
|
"--window-size=%s,%s" % (
|
|
settings.HEADLESS_START_WIDTH,
|
|
settings.HEADLESS_START_HEIGHT,
|
|
)
|
|
)
|
|
else:
|
|
if (
|
|
hasattr(settings, "CHROME_START_WIDTH")
|
|
and isinstance(settings.CHROME_START_WIDTH, int)
|
|
and hasattr(settings, "CHROME_START_HEIGHT")
|
|
and isinstance(settings.CHROME_START_HEIGHT, int)
|
|
):
|
|
chrome_options.add_argument(
|
|
"--window-size=%s,%s" % (
|
|
settings.CHROME_START_WIDTH,
|
|
settings.CHROME_START_HEIGHT,
|
|
)
|
|
)
|
|
if (
|
|
not proxy_auth
|
|
and not disable_csp
|
|
and not ad_block_on
|
|
and not recorder_ext
|
|
and (not extension_zip and not extension_dir)
|
|
):
|
|
if incognito:
|
|
# Use Chrome's Incognito Mode
|
|
# Incognito Mode prevents Chrome extensions from loading,
|
|
# so if using extensions or a feature that uses extensions,
|
|
# then Chrome's Incognito mode will be disabled instead.
|
|
chrome_options.add_argument("--incognito")
|
|
elif guest_mode:
|
|
# Use Chrome's Guest Mode
|
|
# Guest mode prevents Chrome extensions from loading,
|
|
# so if using extensions or a feature that uses extensions,
|
|
# then Chrome's Guest Mode will be disabled instead.
|
|
chrome_options.add_argument("--guest")
|
|
else:
|
|
pass
|
|
if dark_mode:
|
|
chrome_options.add_argument("--enable-features=WebContentsForceDark")
|
|
if user_data_dir and not is_using_uc(undetectable, browser_name):
|
|
abs_path = os.path.abspath(user_data_dir)
|
|
chrome_options.add_argument("--user-data-dir=%s" % abs_path)
|
|
if extension_zip:
|
|
# Can be a comma-separated list of .ZIP or .CRX files
|
|
extension_zip_list = extension_zip.split(",")
|
|
for extension_zip_item in extension_zip_list:
|
|
abs_path = os.path.abspath(extension_zip_item)
|
|
chrome_options.add_extension(abs_path)
|
|
if extension_dir:
|
|
# load-extension input can be a comma-separated list
|
|
abs_path = (
|
|
",".join(os.path.abspath(p) for p in extension_dir.split(","))
|
|
)
|
|
chrome_options = add_chrome_ext_dir(chrome_options, abs_path)
|
|
if (
|
|
page_load_strategy
|
|
and page_load_strategy.lower() in ["eager", "none"]
|
|
):
|
|
# Only change it if not "normal", which is the default.
|
|
chrome_options.page_load_strategy = page_load_strategy.lower()
|
|
elif (
|
|
not page_load_strategy
|
|
and hasattr(settings, "PAGE_LOAD_STRATEGY")
|
|
and settings.PAGE_LOAD_STRATEGY
|
|
and settings.PAGE_LOAD_STRATEGY.lower() in ["eager", "none"]
|
|
):
|
|
# Only change it if not "normal", which is the default.
|
|
chrome_options.page_load_strategy = settings.PAGE_LOAD_STRATEGY.lower()
|
|
if headless2:
|
|
if servername and servername != "localhost":
|
|
# The Grid Node will need Chrome 109 or newer
|
|
chrome_options.add_argument("--headless=new")
|
|
else:
|
|
pass # Processed After Version Check
|
|
elif headless:
|
|
if not undetectable:
|
|
if headless1:
|
|
chrome_options.add_argument("--headless=old")
|
|
else:
|
|
chrome_options.add_argument("--headless")
|
|
if undetectable and servername and servername != "localhost":
|
|
# The Grid Node will need Chrome 109 or newer
|
|
chrome_options.add_argument("--headless=new")
|
|
else:
|
|
pass # Processed After Version Check
|
|
if (settings.DISABLE_CSP_ON_CHROME or disable_csp) and not headless:
|
|
# Headless Chrome does not support extensions, which are required
|
|
# for disabling the Content Security Policy on Chrome.
|
|
if is_using_uc(undetectable, browser_name):
|
|
disable_csp_zip = DISABLE_CSP_ZIP_PATH
|
|
disable_csp_dir = os.path.join(DOWNLOADS_FOLDER, "disable_csp")
|
|
_unzip_to_new_folder(disable_csp_zip, disable_csp_dir)
|
|
chrome_options = add_chrome_ext_dir(
|
|
chrome_options, disable_csp_dir
|
|
)
|
|
else:
|
|
chrome_options = _add_chrome_disable_csp_extension(chrome_options)
|
|
if ad_block_on and not headless:
|
|
# Headless Chrome does not support extensions.
|
|
if is_using_uc(undetectable, browser_name):
|
|
ad_block_zip = AD_BLOCK_ZIP_PATH
|
|
ad_block_dir = os.path.join(DOWNLOADS_FOLDER, "ad_block")
|
|
_unzip_to_new_folder(ad_block_zip, ad_block_dir)
|
|
chrome_options = add_chrome_ext_dir(chrome_options, ad_block_dir)
|
|
else:
|
|
chrome_options = _add_chrome_ad_block_extension(chrome_options)
|
|
if recorder_ext and not headless:
|
|
if is_using_uc(undetectable, browser_name):
|
|
recorder_zip = RECORDER_ZIP_PATH
|
|
recorder_dir = os.path.join(DOWNLOADS_FOLDER, "recorder")
|
|
_unzip_to_new_folder(recorder_zip, recorder_dir)
|
|
chrome_options = add_chrome_ext_dir(chrome_options, recorder_dir)
|
|
else:
|
|
chrome_options = _add_chrome_recorder_extension(chrome_options)
|
|
if chromium_arg and "sbase" in chromium_arg:
|
|
sbase_ext_zip = SBASE_EXT_ZIP_PATH
|
|
sbase_ext_dir = os.path.join(DOWNLOADS_FOLDER, "sbase_ext")
|
|
_unzip_to_new_folder(sbase_ext_zip, sbase_ext_dir)
|
|
chrome_options = add_chrome_ext_dir(chrome_options, sbase_ext_dir)
|
|
if proxy_string:
|
|
if proxy_auth:
|
|
zip_it = True
|
|
if is_using_uc(undetectable, browser_name):
|
|
zip_it = False # undetected-chromedriver needs a folder ext
|
|
chrome_options = _add_chrome_proxy_extension(
|
|
chrome_options,
|
|
proxy_string,
|
|
proxy_user,
|
|
proxy_pass,
|
|
proxy_bypass_list,
|
|
zip_it,
|
|
multi_proxy,
|
|
)
|
|
chrome_options.add_argument("--proxy-server=%s" % proxy_string)
|
|
if proxy_bypass_list:
|
|
chrome_options.add_argument(
|
|
"--proxy-bypass-list=%s" % proxy_bypass_list
|
|
)
|
|
elif proxy_pac_url:
|
|
if proxy_auth:
|
|
zip_it = True # undetected-chromedriver needs a folder ext
|
|
if is_using_uc(undetectable, browser_name):
|
|
zip_it = False
|
|
chrome_options = _add_chrome_proxy_extension(
|
|
chrome_options,
|
|
None,
|
|
proxy_user,
|
|
proxy_pass,
|
|
proxy_bypass_list,
|
|
zip_it,
|
|
multi_proxy,
|
|
)
|
|
chrome_options.add_argument("--proxy-pac-url=%s" % proxy_pac_url)
|
|
if (
|
|
not is_using_uc(undetectable, browser_name)
|
|
or not enable_ws
|
|
or proxy_string
|
|
):
|
|
chrome_options.add_argument("--ignore-certificate-errors")
|
|
chrome_options.add_argument("--ignore-ssl-errors=yes")
|
|
if not enable_ws:
|
|
chrome_options.add_argument("--disable-web-security")
|
|
if (
|
|
IS_LINUX
|
|
or (IS_MAC and not is_using_uc(undetectable, browser_name))
|
|
):
|
|
chrome_options.add_argument("--no-sandbox")
|
|
if remote_debug:
|
|
# To access the Debugger, go to: chrome://inspect/#devices
|
|
# while a Chromium driver is running.
|
|
# Info: https://chromedevtools.github.io/devtools-protocol/
|
|
args = " ".join(sys.argv)
|
|
debug_port = 9222
|
|
if ("-n" in sys.argv or " -n=" in args or args == "-c"):
|
|
debug_port = service_utils.free_port()
|
|
chrome_options.add_argument("--remote-debugging-port=%s" % debug_port)
|
|
if swiftshader:
|
|
chrome_options.add_argument("--use-gl=angle")
|
|
chrome_options.add_argument("--use-angle=swiftshader-webgl")
|
|
elif (
|
|
not is_using_uc(undetectable, browser_name)
|
|
and not enable_3d_apis
|
|
):
|
|
chrome_options.add_argument("--disable-gpu")
|
|
if (
|
|
(not IS_LINUX and is_using_uc(undetectable, browser_name))
|
|
or (
|
|
IS_MAC
|
|
and binary_location
|
|
and "chrome-headless-shell" in binary_location
|
|
)
|
|
):
|
|
chrome_options.add_argument("--disable-dev-shm-usage")
|
|
chrome_options.add_argument("--disable-application-cache")
|
|
if IS_LINUX:
|
|
chrome_options.add_argument("--disable-dev-shm-usage")
|
|
if is_using_uc(undetectable, browser_name):
|
|
chrome_options.add_argument("--disable-application-cache")
|
|
chrome_options.add_argument("--disable-setuid-sandbox")
|
|
if not binary_location:
|
|
br_app = "google-chrome"
|
|
binary_loc = detect_b_ver.get_binary_location(br_app, True)
|
|
if os.path.exists(binary_loc):
|
|
binary_location = binary_loc
|
|
elif os.path.exists("/usr/bin/google-chrome-stable"):
|
|
binary_location = "/usr/bin/google-chrome-stable"
|
|
elif os.path.exists("/usr/bin/google-chrome"):
|
|
binary_location = "/usr/bin/google-chrome"
|
|
extra_disabled_features = []
|
|
if chromium_arg:
|
|
# Can be a comma-separated list of Chromium args or a list
|
|
chromium_arg_list = None
|
|
if isinstance(chromium_arg, (list, tuple)):
|
|
chromium_arg_list = chromium_arg
|
|
else:
|
|
chromium_arg_list = chromium_arg.split(",")
|
|
for chromium_arg_item in chromium_arg_list:
|
|
chromium_arg_item = chromium_arg_item.strip()
|
|
if not chromium_arg_item.startswith("--"):
|
|
if chromium_arg_item.startswith("-"):
|
|
chromium_arg_item = "-" + chromium_arg_item
|
|
else:
|
|
chromium_arg_item = "--" + chromium_arg_item
|
|
if "remote-debugging-port=" in chromium_arg_item:
|
|
with suppress(Exception):
|
|
# Extra processing for UC Mode
|
|
chrome_options._remote_debugging_port = int(
|
|
chromium_arg_item.split("remote-debugging-port=")[1]
|
|
)
|
|
if "set-binary" in chromium_arg_item and not binary_location:
|
|
br_app = "google-chrome"
|
|
binary_loc = detect_b_ver.get_binary_location(
|
|
br_app, is_using_uc(undetectable, browser_name)
|
|
)
|
|
if os.path.exists(binary_loc):
|
|
binary_location = binary_loc
|
|
elif "disable-features=" in chromium_arg_item:
|
|
d_f = chromium_arg_item.split("disable-features=")[-1]
|
|
extra_disabled_features.append(d_f)
|
|
elif len(chromium_arg_item) >= 3:
|
|
chrome_options.add_argument(chromium_arg_item)
|
|
if disable_features:
|
|
extra_disabled_features.extend(disable_features.split(","))
|
|
if devtools and not headless:
|
|
chrome_options.add_argument("--auto-open-devtools-for-tabs")
|
|
if user_agent:
|
|
chrome_options.add_argument("--user-agent=%s" % user_agent)
|
|
chrome_options.add_argument("--safebrowsing-disable-download-protection")
|
|
chrome_options.add_argument("--disable-search-engine-choice-screen")
|
|
chrome_options.add_argument("--disable-browser-side-navigation")
|
|
chrome_options.add_argument("--disable-save-password-bubble")
|
|
chrome_options.add_argument("--disable-single-click-autofill")
|
|
chrome_options.add_argument("--allow-file-access-from-files")
|
|
chrome_options.add_argument("--disable-prompt-on-repost")
|
|
chrome_options.add_argument("--dns-prefetch-disable")
|
|
chrome_options.add_argument("--disable-translate")
|
|
if binary_location:
|
|
chrome_options.binary_location = binary_location
|
|
if not enable_3d_apis and not is_using_uc(undetectable, browser_name):
|
|
chrome_options.add_argument("--disable-3d-apis")
|
|
if headless or headless2 or is_using_uc(undetectable, browser_name):
|
|
chrome_options.add_argument("--disable-renderer-backgrounding")
|
|
chrome_options.add_argument("--disable-backgrounding-occluded-windows")
|
|
chrome_options.add_argument("--disable-client-side-phishing-detection")
|
|
chrome_options.add_argument("--disable-oopr-debug-crash-dump")
|
|
chrome_options.add_argument("--disable-top-sites")
|
|
chrome_options.add_argument("--ash-no-nudges")
|
|
chrome_options.add_argument("--no-crash-upload")
|
|
chrome_options.add_argument("--deny-permission-prompts")
|
|
chrome_options.add_argument(
|
|
'--simulate-outdated-no-au="Tue, 31 Dec 2099 23:59:59 GMT"'
|
|
)
|
|
chrome_options.add_argument("--disable-ipc-flooding-protection")
|
|
chrome_options.add_argument("--disable-password-generation")
|
|
chrome_options.add_argument("--disable-domain-reliability")
|
|
chrome_options.add_argument("--disable-breakpad")
|
|
included_disabled_features = []
|
|
included_disabled_features.append("OptimizationHints")
|
|
included_disabled_features.append("OptimizationHintsFetching")
|
|
included_disabled_features.append("Translate")
|
|
included_disabled_features.append("OptimizationTargetPrediction")
|
|
included_disabled_features.append("OptimizationGuideModelDownloading")
|
|
included_disabled_features.append("DownloadBubble")
|
|
included_disabled_features.append("DownloadBubbleV2")
|
|
included_disabled_features.append("InsecureDownloadWarnings")
|
|
included_disabled_features.append("InterestFeedContentSuggestions")
|
|
included_disabled_features.append("PrivacySandboxSettings4")
|
|
included_disabled_features.append("SidePanelPinning")
|
|
included_disabled_features.append("UserAgentClientHint")
|
|
for item in extra_disabled_features:
|
|
if item not in included_disabled_features:
|
|
included_disabled_features.append(item)
|
|
d_f_string = ",".join(included_disabled_features)
|
|
chrome_options.add_argument("--disable-features=%s" % d_f_string)
|
|
if (
|
|
is_using_uc(undetectable, browser_name)
|
|
and (
|
|
not headless
|
|
or IS_LINUX # switches to Xvfb (non-headless)
|
|
)
|
|
):
|
|
chrome_options.add_argument("--no-pings")
|
|
chrome_options.add_argument("--homepage=chrome://version/")
|
|
chrome_options.add_argument("--animation-duration-scale=0")
|
|
chrome_options.add_argument("--wm-window-animations-disabled")
|
|
chrome_options.add_argument("--enable-privacy-sandbox-ads-apis")
|
|
chrome_options.add_argument("--disable-background-timer-throttling")
|
|
# Prevent new tabs opened by Selenium from being blocked:
|
|
chrome_options.add_argument("--disable-popup-blocking")
|
|
# Skip remaining options that trigger anti-bot services
|
|
return chrome_options
|
|
chrome_options.add_argument("--test-type")
|
|
chrome_options.add_argument("--log-level=3")
|
|
chrome_options.add_argument("--no-first-run")
|
|
chrome_options.add_argument("--allow-insecure-localhost")
|
|
chrome_options.add_argument("--allow-running-insecure-content")
|
|
chrome_options.add_argument("--disable-infobars")
|
|
chrome_options.add_argument("--disable-notifications")
|
|
chrome_options.add_argument(
|
|
"--disable-autofill-keyboard-accessory-view[8]"
|
|
)
|
|
chrome_options.add_argument("--homepage=about:blank")
|
|
chrome_options.add_argument("--dom-automation")
|
|
chrome_options.add_argument("--disable-hang-monitor")
|
|
return chrome_options
|
|
|
|
|
|
def _set_firefox_options(
|
|
downloads_path,
|
|
headless,
|
|
locale_code,
|
|
proxy_string,
|
|
proxy_bypass_list,
|
|
proxy_pac_url,
|
|
user_agent,
|
|
disable_cookies,
|
|
disable_js,
|
|
disable_csp,
|
|
firefox_arg,
|
|
firefox_pref,
|
|
external_pdf,
|
|
):
|
|
blank_p = "about:blank"
|
|
options = webdriver.FirefoxOptions()
|
|
options.accept_untrusted_certs = True
|
|
options.set_preference("reader.parse-on-load.enabled", False)
|
|
options.set_preference("browser.startup.homepage", blank_p)
|
|
options.set_preference("startup.homepage_welcome_url", blank_p)
|
|
options.set_preference("startup.homepage_welcome_url.additional", blank_p)
|
|
options.set_preference("browser.newtab.url", blank_p)
|
|
options.set_preference("trailhead.firstrun.branches", "nofirstrun-empty")
|
|
options.set_preference("browser.aboutwelcome.enabled", False)
|
|
options.set_preference("app.update.auto", False)
|
|
options.set_preference("app.update.enabled", False)
|
|
options.set_preference("browser.formfill.enable", False)
|
|
options.set_preference("browser.privatebrowsing.autostart", True)
|
|
options.set_preference("dom.webnotifications.enabled", False)
|
|
options.set_preference("dom.disable_beforeunload", True)
|
|
options.set_preference("browser.contentblocking.database.enabled", True)
|
|
options.set_preference("extensions.systemAddon.update.enabled", False)
|
|
options.set_preference("extensions.update.autoUpdateDefault", False)
|
|
options.set_preference("extensions.update.enabled", False)
|
|
options.set_preference("datareporting.healthreport.service.enabled", False)
|
|
options.set_preference("datareporting.healthreport.uploadEnabled", False)
|
|
options.set_preference("datareporting.policy.dataSubmissionEnabled", False)
|
|
options.set_preference("browser.search.update", False)
|
|
options.set_preference("privacy.trackingprotection.enabled", False)
|
|
options.set_preference("toolkit.telemetry.enabled", False)
|
|
options.set_preference("toolkit.telemetry.unified", False)
|
|
options.set_preference("toolkit.telemetry.archive.enabled", False)
|
|
if proxy_string:
|
|
socks_proxy = False
|
|
socks_ver = 0
|
|
chunks = proxy_string.split(":")
|
|
if len(chunks) == 3 and (
|
|
chunks[0] == "socks4" or chunks[0] == "socks5"
|
|
):
|
|
socks_proxy = True
|
|
socks_ver = int(chunks[0][5])
|
|
if chunks[1].startswith("//") and len(chunks[1]) > 2:
|
|
chunks[1] = chunks[1][2:]
|
|
proxy_server = chunks[1]
|
|
proxy_port = chunks[2]
|
|
else:
|
|
proxy_server = proxy_string.split(":")[0]
|
|
proxy_port = proxy_string.split(":")[1]
|
|
options.set_preference("network.proxy.type", 1)
|
|
if socks_proxy:
|
|
options.set_preference("network.proxy.socks", proxy_server)
|
|
options.set_preference("network.proxy.socks_port", int(proxy_port))
|
|
options.set_preference("network.proxy.socks_version", socks_ver)
|
|
else:
|
|
options.set_preference("network.proxy.http", proxy_server)
|
|
options.set_preference("network.proxy.http_port", int(proxy_port))
|
|
options.set_preference("network.proxy.ssl", proxy_server)
|
|
options.set_preference("network.proxy.ssl_port", int(proxy_port))
|
|
if proxy_bypass_list:
|
|
options.set_preference("no_proxies_on", proxy_bypass_list)
|
|
elif proxy_pac_url:
|
|
options.set_preference("network.proxy.type", 2)
|
|
options.set_preference("network.proxy.autoconfig_url", proxy_pac_url)
|
|
if user_agent:
|
|
options.set_preference("general.useragent.override", user_agent)
|
|
options.set_preference(
|
|
"security.mixed_content.block_active_content", False
|
|
)
|
|
options.set_preference("security.warn_submit_insecure", False)
|
|
if disable_cookies:
|
|
options.set_preference("network.cookie.cookieBehavior", 2)
|
|
if disable_js:
|
|
options.set_preference("javascript.enabled", False)
|
|
if settings.DISABLE_CSP_ON_FIREFOX or disable_csp:
|
|
options.set_preference("security.csp.enable", False)
|
|
options.set_preference(
|
|
"browser.download.manager.showAlertOnComplete", False
|
|
)
|
|
if headless and not IS_LINUX:
|
|
options.add_argument("--headless")
|
|
elif headless and IS_LINUX:
|
|
# This assumes Xvfb is running, which prevents many Linux issues.
|
|
# If not, we'll fix this later during the error-handling process.
|
|
# To override this feature: ``pytest --firefox-arg="-headless"``.
|
|
pass
|
|
if locale_code:
|
|
options.set_preference("intl.accept_languages", locale_code)
|
|
options.set_preference("browser.shell.checkDefaultBrowser", False)
|
|
options.set_preference("browser.startup.page", 0)
|
|
options.set_preference("browser.download.panel.shown", False)
|
|
options.set_preference("browser.download.animateNotifications", False)
|
|
options.set_preference("browser.download.dir", downloads_path)
|
|
options.set_preference("browser.download.folderList", 2)
|
|
options.set_preference("browser.helperApps.alwaysAsk.force", False)
|
|
options.set_preference("browser.download.manager.showWhenStarting", False)
|
|
options.set_preference(
|
|
"browser.helperApps.neverAsk.saveToDisk",
|
|
(
|
|
"application/pdf,application/zip,application/octet-stream,"
|
|
"text/csv,text/xml,application/xml,text/plain,application/json,"
|
|
"text/octet-stream,application/x-gzip,application/x-tar,"
|
|
"application/java-archive,text/x-java-source,java,"
|
|
"application/javascript,video/jpeg,audio/x-aac,image/svg+xml,"
|
|
"application/x-font-woff,application/x-7z-compressed,"
|
|
"application/mp4,video/mp4,audio/mp4,video/x-msvideo,"
|
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
),
|
|
)
|
|
if external_pdf:
|
|
options.set_preference("pdfjs.disabled", True)
|
|
else:
|
|
options.set_preference("pdfjs.disabled", False)
|
|
if firefox_arg:
|
|
# Can be a comma-separated list of Firefox args
|
|
firefox_arg_list = firefox_arg.split(",")
|
|
for firefox_arg_item in firefox_arg_list:
|
|
firefox_arg_item = firefox_arg_item.strip()
|
|
if not firefox_arg_item.startswith("-"):
|
|
if firefox_arg_item.count(os.sep) == 0:
|
|
firefox_arg_item = "--" + firefox_arg_item
|
|
if len(firefox_arg_item) >= 3:
|
|
options.add_argument(firefox_arg_item)
|
|
if firefox_pref:
|
|
# Can be a comma-separated list of Firefox preference:value pairs
|
|
firefox_pref_list = firefox_pref.split(",")
|
|
for firefox_pref_item in firefox_pref_list:
|
|
f_pref = None
|
|
f_pref_value = None
|
|
needs_conversion = False
|
|
if firefox_pref_item.count(":") == 0:
|
|
f_pref = firefox_pref_item
|
|
f_pref_value = True
|
|
elif firefox_pref_item.count(":") == 1:
|
|
f_pref = firefox_pref_item.split(":")[0]
|
|
f_pref_value = firefox_pref_item.split(":")[1]
|
|
needs_conversion = True
|
|
elif firefox_pref_item.count("://") == 1:
|
|
f_pref = firefox_pref_item.split(":")[0]
|
|
f_pref_value = ":".join(firefox_pref_item.split(":")[1:])
|
|
else: # More than one ":" in the set without a URL.
|
|
raise Exception(
|
|
'Incorrect formatting for Firefox "pref:value" set!'
|
|
)
|
|
if needs_conversion:
|
|
if f_pref_value.lower() == "true" or len(f_pref_value) == 0:
|
|
f_pref_value = True
|
|
elif f_pref_value.lower() == "false":
|
|
f_pref_value = False
|
|
elif f_pref_value.isdigit():
|
|
f_pref_value = int(f_pref_value)
|
|
elif f_pref_value.replace(".", "", 1).isdigit():
|
|
f_pref_value = float(f_pref_value)
|
|
else:
|
|
pass # keep as string
|
|
if len(f_pref) >= 1:
|
|
options.set_preference(f_pref, f_pref_value)
|
|
return options
|
|
|
|
|
|
def get_driver(
|
|
browser_name=None,
|
|
headless=False,
|
|
locale_code=None,
|
|
use_grid=False,
|
|
protocol="http",
|
|
servername="localhost",
|
|
port=4444,
|
|
proxy_string=None,
|
|
proxy_bypass_list=None,
|
|
proxy_pac_url=None,
|
|
multi_proxy=None,
|
|
user_agent=None,
|
|
cap_file=None,
|
|
cap_string=None,
|
|
recorder_ext=False,
|
|
disable_cookies=False,
|
|
disable_js=False,
|
|
disable_csp=False,
|
|
enable_ws=False,
|
|
enable_sync=False,
|
|
use_auto_ext=False,
|
|
undetectable=False,
|
|
uc_cdp_events=False,
|
|
uc_subprocess=False,
|
|
log_cdp_events=False,
|
|
no_sandbox=False,
|
|
disable_gpu=False,
|
|
headless1=False,
|
|
headless2=False,
|
|
incognito=False,
|
|
guest_mode=False,
|
|
dark_mode=False,
|
|
devtools=False,
|
|
remote_debug=False,
|
|
enable_3d_apis=False,
|
|
swiftshader=False,
|
|
ad_block_on=False,
|
|
host_resolver_rules=None,
|
|
block_images=False,
|
|
do_not_track=False,
|
|
chromium_arg=None,
|
|
firefox_arg=None,
|
|
firefox_pref=None,
|
|
user_data_dir=None,
|
|
extension_zip=None,
|
|
extension_dir=None,
|
|
disable_features=None,
|
|
binary_location=None,
|
|
driver_version=None,
|
|
page_load_strategy=None,
|
|
use_wire=False,
|
|
external_pdf=False,
|
|
test_id=None,
|
|
mobile_emulator=False,
|
|
device_width=None,
|
|
device_height=None,
|
|
device_pixel_ratio=None,
|
|
browser=None, # A duplicate of browser_name to avoid confusion
|
|
):
|
|
if not browser_name:
|
|
if browser:
|
|
browser_name = browser
|
|
else:
|
|
browser_name = "chrome" # The default if not specified
|
|
browser_name = browser_name.lower()
|
|
if headless2 and browser_name == constants.Browser.FIREFOX:
|
|
headless2 = False # Only for Chromium
|
|
headless = True
|
|
if (
|
|
is_using_uc(undetectable, browser_name)
|
|
and binary_location
|
|
and isinstance(binary_location, str)
|
|
and binary_location.lower() == "chs"
|
|
):
|
|
raise Exception("UC Mode can't be used with Chrome-Headless-Shell!")
|
|
if (
|
|
binary_location
|
|
and isinstance(binary_location, str)
|
|
and (
|
|
browser_name == constants.Browser.GOOGLE_CHROME
|
|
or browser_name == constants.Browser.EDGE
|
|
)
|
|
):
|
|
if (
|
|
binary_location.lower() == "cft"
|
|
and browser_name == constants.Browser.GOOGLE_CHROME
|
|
):
|
|
binary_folder = None
|
|
if IS_MAC:
|
|
if IS_ARM_MAC:
|
|
binary_folder = "chrome-mac-arm64"
|
|
else:
|
|
binary_folder = "chrome-mac-x64"
|
|
elif IS_LINUX:
|
|
binary_folder = "chrome-linux64"
|
|
elif IS_WINDOWS:
|
|
if "64" in ARCH:
|
|
binary_folder = "chrome-win64"
|
|
else:
|
|
binary_folder = "chrome-win32"
|
|
if binary_folder:
|
|
binary_location = os.path.join(DRIVER_DIR, binary_folder)
|
|
if not os.path.exists(binary_location):
|
|
from seleniumbase.console_scripts import sb_install
|
|
args = " ".join(sys.argv)
|
|
if not (
|
|
"-n" in sys.argv or " -n=" in args or args == "-c"
|
|
):
|
|
# (Not multithreaded)
|
|
sys_args = sys.argv # Save a copy of current sys args
|
|
log_d(
|
|
"\nWarning: Chrome for Testing binary not found..."
|
|
)
|
|
try:
|
|
sb_install.main(override="cft")
|
|
except Exception as e:
|
|
log_d("\nWarning: Chrome download failed: %s" % e)
|
|
sys.argv = sys_args # Put back the original sys args
|
|
else:
|
|
chrome_fixing_lock = fasteners.InterProcessLock(
|
|
constants.MultiBrowser.DRIVER_FIXING_LOCK
|
|
)
|
|
with chrome_fixing_lock:
|
|
with suppress(Exception):
|
|
shared_utils.make_writable(
|
|
constants.MultiBrowser.DRIVER_FIXING_LOCK
|
|
)
|
|
if not os.path.exists(binary_location):
|
|
sys_args = sys.argv # Save a copy of sys args
|
|
log_d(
|
|
"\nWarning: "
|
|
"Chrome for Testing binary not found..."
|
|
)
|
|
sb_install.main(override="cft")
|
|
sys.argv = sys_args # Put back original args
|
|
else:
|
|
binary_location = None
|
|
if (
|
|
binary_location.lower() == "chs"
|
|
and browser_name == constants.Browser.GOOGLE_CHROME
|
|
):
|
|
binary_folder = None
|
|
if IS_MAC:
|
|
if IS_ARM_MAC:
|
|
binary_folder = "chrome-headless-shell-mac-arm64"
|
|
else:
|
|
binary_folder = "chrome-headless-shell-mac-x64"
|
|
elif IS_LINUX:
|
|
binary_folder = "chrome-headless-shell-linux64"
|
|
elif IS_WINDOWS:
|
|
if "64" in ARCH:
|
|
binary_folder = "chrome-headless-shell-win64"
|
|
else:
|
|
binary_folder = "chrome-headless-shell-win32"
|
|
if binary_folder:
|
|
binary_location = os.path.join(DRIVER_DIR, binary_folder)
|
|
if not os.path.exists(binary_location):
|
|
from seleniumbase.console_scripts import sb_install
|
|
args = " ".join(sys.argv)
|
|
if not (
|
|
"-n" in sys.argv or " -n=" in args or args == "-c"
|
|
):
|
|
# (Not multithreaded)
|
|
sys_args = sys.argv # Save a copy of current sys args
|
|
log_d(
|
|
"\nWarning: "
|
|
"Chrome-Headless-Shell binary not found..."
|
|
)
|
|
try:
|
|
sb_install.main(override="chs")
|
|
except Exception as e:
|
|
log_d(
|
|
"\nWarning: "
|
|
"Chrome-Headless-Shell download failed: %s" % e
|
|
)
|
|
sys.argv = sys_args # Put back the original sys args
|
|
else:
|
|
chrome_fixing_lock = fasteners.InterProcessLock(
|
|
constants.MultiBrowser.DRIVER_FIXING_LOCK
|
|
)
|
|
with chrome_fixing_lock:
|
|
with suppress(Exception):
|
|
shared_utils.make_writable(
|
|
constants.MultiBrowser.DRIVER_FIXING_LOCK
|
|
)
|
|
if not os.path.exists(binary_location):
|
|
sys_args = sys.argv # Save a copy of sys args
|
|
log_d(
|
|
"\nWarning: "
|
|
"Chrome-Headless-Shell binary not found..."
|
|
)
|
|
sb_install.main(override="chs")
|
|
sys.argv = sys_args # Put back original args
|
|
else:
|
|
binary_location = None
|
|
if not os.path.exists(binary_location):
|
|
log_d(
|
|
"\nWarning: The Chromium binary specified (%s) was NOT found!"
|
|
"\n(Will use default settings...)\n" % binary_location
|
|
)
|
|
binary_location = None
|
|
elif binary_location.endswith("/") or binary_location.endswith("\\"):
|
|
log_d(
|
|
"\nWarning: The Chromium binary path must be a full path "
|
|
"that includes the browser filename at the end of it!"
|
|
"\n(Will use default settings...)\n" % binary_location
|
|
)
|
|
# Example of a valid binary location path - MacOS:
|
|
# "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
|
binary_location = None
|
|
else:
|
|
binary_name = binary_location.split("/")[-1].split("\\")[-1]
|
|
valid_names = get_valid_binary_names_for_browser(browser_name)
|
|
if binary_name == "Google Chrome for Testing.app":
|
|
binary_name = "Google Chrome for Testing"
|
|
binary_location += "/Contents/MacOS/Google Chrome for Testing"
|
|
elif binary_name in ["chrome-mac-arm64", "chrome-mac-x64"]:
|
|
binary_name = "Google Chrome for Testing"
|
|
binary_location += "/Google Chrome for Testing.app"
|
|
binary_location += "/Contents/MacOS/Google Chrome for Testing"
|
|
elif binary_name == "chrome-linux64":
|
|
binary_name = "chrome"
|
|
binary_location += "/chrome"
|
|
elif binary_name in ["chrome-win32", "chrome-win64"]:
|
|
binary_name = "chrome.exe"
|
|
binary_location += "\\chrome.exe"
|
|
elif binary_name in [
|
|
"chrome-headless-shell-mac-arm64",
|
|
"chrome-headless-shell-mac-x64",
|
|
]:
|
|
binary_name = "chrome-headless-shell"
|
|
binary_location += "/chrome-headless-shell"
|
|
elif binary_name == "chrome-headless-shell-linux64":
|
|
binary_name = "chrome-headless-shell"
|
|
binary_location += "/chrome-headless-shell"
|
|
elif binary_name in [
|
|
"chrome-headless-shell-win32",
|
|
"chrome-headless-shell-win64",
|
|
]:
|
|
binary_name = "chrome-headless-shell.exe"
|
|
binary_location += "\\chrome-headless-shell.exe"
|
|
if binary_name not in valid_names:
|
|
log_d(
|
|
"\nWarning: The Chromium binary specified is NOT valid!"
|
|
'\nExpecting "%s" to be found in %s for the browser / OS!'
|
|
"\n(Will use default settings...)\n"
|
|
"" % (binary_name, valid_names)
|
|
)
|
|
binary_location = None
|
|
elif binary_location.lower() == "chs":
|
|
headless = True
|
|
if (uc_cdp_events or uc_subprocess) and not undetectable:
|
|
undetectable = True
|
|
if mobile_emulator and not user_agent:
|
|
# Use a Pixel user agent by default if not specified
|
|
user_agent = constants.Mobile.AGENT
|
|
if page_load_strategy and page_load_strategy.lower() == "none":
|
|
settings.PAGE_LOAD_STRATEGY = "none"
|
|
proxy_auth = False
|
|
proxy_user = None
|
|
proxy_pass = None
|
|
if proxy_string:
|
|
username_and_password = None
|
|
if "@" in proxy_string:
|
|
# Format => username:password@hostname:port
|
|
try:
|
|
username_and_password = proxy_string.split("@")[0]
|
|
proxy_string = proxy_string.split("@")[1]
|
|
proxy_user = username_and_password.split(":")[0]
|
|
proxy_pass = username_and_password.split(":")[1]
|
|
except Exception:
|
|
raise Exception(
|
|
"The format for using a proxy server with authentication "
|
|
'is: "username:password@hostname:port". If using a proxy '
|
|
'server without auth, the format is: "hostname:port".'
|
|
)
|
|
if browser_name != constants.Browser.GOOGLE_CHROME and (
|
|
browser_name != constants.Browser.EDGE
|
|
):
|
|
raise Exception(
|
|
"Chrome or Edge is required when using a proxy server "
|
|
"that has authentication! (If using a proxy server "
|
|
"without auth, Chrome, Edge, or Firefox may be used.)"
|
|
)
|
|
proxy_string = proxy_helper.validate_proxy_string(proxy_string)
|
|
if proxy_string and proxy_user and proxy_pass:
|
|
proxy_auth = True
|
|
elif proxy_pac_url:
|
|
username_and_password = None
|
|
if "@" in proxy_pac_url:
|
|
# Format => username:password@PAC_URL.pac
|
|
try:
|
|
username_and_password = proxy_pac_url.split("@")[0]
|
|
proxy_pac_url = proxy_pac_url.split("@")[1]
|
|
proxy_user = username_and_password.split(":")[0]
|
|
proxy_pass = username_and_password.split(":")[1]
|
|
except Exception:
|
|
raise Exception(
|
|
"The format for using a PAC URL with authentication "
|
|
'is: "username:password@PAC_URL.pac". If using a PAC '
|
|
'URL without auth, the format is: "PAC_URL.pac".'
|
|
)
|
|
if browser_name != constants.Browser.GOOGLE_CHROME and (
|
|
browser_name != constants.Browser.EDGE
|
|
):
|
|
raise Exception(
|
|
"Chrome or Edge is required when using a PAC URL "
|
|
"that has authentication! (If using a PAC URL "
|
|
"without auth, Chrome, Edge, or Firefox may be used.)"
|
|
)
|
|
if not proxy_pac_url.lower().endswith(".pac"):
|
|
raise Exception('The proxy PAC URL must end with ".pac"!')
|
|
if proxy_pac_url and proxy_user and proxy_pass:
|
|
proxy_auth = True
|
|
if (
|
|
is_using_uc(undetectable, browser_name)
|
|
and not IS_LINUX
|
|
and headless
|
|
):
|
|
headless = False
|
|
headless2 = True
|
|
if (
|
|
headless
|
|
and (
|
|
proxy_auth
|
|
or disable_cookies
|
|
or disable_js
|
|
or ad_block_on
|
|
or disable_csp
|
|
or recorder_ext
|
|
or extension_zip
|
|
or extension_dir
|
|
)
|
|
and (
|
|
browser_name == constants.Browser.GOOGLE_CHROME
|
|
or browser_name == constants.Browser.EDGE
|
|
)
|
|
):
|
|
# Headless Chrome/Edge doesn't support extensions, which are
|
|
# required when using a proxy server that has authentication,
|
|
# or when using other SeleniumBase extensions (eg: Recorder).
|
|
# Instead, base_case.py will use the SBVirtualDisplay when not
|
|
# using Chrome's built-in headless mode. See link for details:
|
|
# https://bugs.chromium.org/p/chromium/issues/detail?id=706008
|
|
headless = False
|
|
if not IS_LINUX:
|
|
# Use the new headless mode on Chrome if not using Linux:
|
|
# bugs.chromium.org/p/chromium/issues/detail?id=706008#c36
|
|
# Although Linux is technically supported, there are a lot
|
|
# of old versions of Chrome on Linux server machines, and
|
|
# this mode requires a recent version of Chrome to work.
|
|
# Specify "--headless2" as a pytest arg to use on Linux.
|
|
headless2 = True
|
|
if (
|
|
browser_name == constants.Browser.GOOGLE_CHROME
|
|
and user_data_dir
|
|
and len(user_data_dir) < 3
|
|
):
|
|
raise Exception(
|
|
"Name length of Chrome's User Data Directory must be >= 3."
|
|
)
|
|
if use_grid:
|
|
return get_remote_driver(
|
|
browser_name,
|
|
headless,
|
|
locale_code,
|
|
protocol,
|
|
servername,
|
|
port,
|
|
proxy_string,
|
|
proxy_auth,
|
|
proxy_user,
|
|
proxy_pass,
|
|
proxy_bypass_list,
|
|
proxy_pac_url,
|
|
multi_proxy,
|
|
user_agent,
|
|
cap_file,
|
|
cap_string,
|
|
recorder_ext,
|
|
disable_cookies,
|
|
disable_js,
|
|
disable_csp,
|
|
enable_ws,
|
|
enable_sync,
|
|
use_auto_ext,
|
|
undetectable,
|
|
uc_cdp_events,
|
|
uc_subprocess,
|
|
log_cdp_events,
|
|
no_sandbox,
|
|
disable_gpu,
|
|
headless1,
|
|
headless2,
|
|
incognito,
|
|
guest_mode,
|
|
dark_mode,
|
|
devtools,
|
|
remote_debug,
|
|
enable_3d_apis,
|
|
swiftshader,
|
|
ad_block_on,
|
|
host_resolver_rules,
|
|
block_images,
|
|
do_not_track,
|
|
chromium_arg,
|
|
firefox_arg,
|
|
firefox_pref,
|
|
user_data_dir,
|
|
extension_zip,
|
|
extension_dir,
|
|
disable_features,
|
|
binary_location,
|
|
driver_version,
|
|
page_load_strategy,
|
|
use_wire,
|
|
external_pdf,
|
|
test_id,
|
|
mobile_emulator,
|
|
device_width,
|
|
device_height,
|
|
device_pixel_ratio,
|
|
)
|
|
else:
|
|
return get_local_driver(
|
|
browser_name,
|
|
headless,
|
|
locale_code,
|
|
servername,
|
|
proxy_string,
|
|
proxy_auth,
|
|
proxy_user,
|
|
proxy_pass,
|
|
proxy_bypass_list,
|
|
proxy_pac_url,
|
|
multi_proxy,
|
|
user_agent,
|
|
recorder_ext,
|
|
disable_cookies,
|
|
disable_js,
|
|
disable_csp,
|
|
enable_ws,
|
|
enable_sync,
|
|
use_auto_ext,
|
|
undetectable,
|
|
uc_cdp_events,
|
|
uc_subprocess,
|
|
log_cdp_events,
|
|
no_sandbox,
|
|
disable_gpu,
|
|
headless1,
|
|
headless2,
|
|
incognito,
|
|
guest_mode,
|
|
dark_mode,
|
|
devtools,
|
|
remote_debug,
|
|
enable_3d_apis,
|
|
swiftshader,
|
|
ad_block_on,
|
|
host_resolver_rules,
|
|
block_images,
|
|
do_not_track,
|
|
chromium_arg,
|
|
firefox_arg,
|
|
firefox_pref,
|
|
user_data_dir,
|
|
extension_zip,
|
|
extension_dir,
|
|
disable_features,
|
|
binary_location,
|
|
driver_version,
|
|
page_load_strategy,
|
|
use_wire,
|
|
external_pdf,
|
|
mobile_emulator,
|
|
device_width,
|
|
device_height,
|
|
device_pixel_ratio,
|
|
)
|
|
|
|
|
|
def get_remote_driver(
|
|
browser_name,
|
|
headless,
|
|
locale_code,
|
|
protocol,
|
|
servername,
|
|
port,
|
|
proxy_string,
|
|
proxy_auth,
|
|
proxy_user,
|
|
proxy_pass,
|
|
proxy_bypass_list,
|
|
proxy_pac_url,
|
|
multi_proxy,
|
|
user_agent,
|
|
cap_file,
|
|
cap_string,
|
|
recorder_ext,
|
|
disable_cookies,
|
|
disable_js,
|
|
disable_csp,
|
|
enable_ws,
|
|
enable_sync,
|
|
use_auto_ext,
|
|
undetectable,
|
|
uc_cdp_events,
|
|
uc_subprocess,
|
|
log_cdp_events,
|
|
no_sandbox,
|
|
disable_gpu,
|
|
headless1,
|
|
headless2,
|
|
incognito,
|
|
guest_mode,
|
|
dark_mode,
|
|
devtools,
|
|
remote_debug,
|
|
enable_3d_apis,
|
|
swiftshader,
|
|
ad_block_on,
|
|
host_resolver_rules,
|
|
block_images,
|
|
do_not_track,
|
|
chromium_arg,
|
|
firefox_arg,
|
|
firefox_pref,
|
|
user_data_dir,
|
|
extension_zip,
|
|
extension_dir,
|
|
disable_features,
|
|
binary_location,
|
|
driver_version,
|
|
page_load_strategy,
|
|
use_wire,
|
|
external_pdf,
|
|
test_id,
|
|
mobile_emulator,
|
|
device_width,
|
|
device_height,
|
|
device_pixel_ratio,
|
|
):
|
|
if use_wire:
|
|
pip_find_lock = fasteners.InterProcessLock(
|
|
constants.PipInstall.FINDLOCK
|
|
)
|
|
with pip_find_lock: # Prevent issues with multiple processes
|
|
with suppress(Exception):
|
|
shared_utils.make_writable(constants.PipInstall.FINDLOCK)
|
|
try:
|
|
from seleniumwire import webdriver
|
|
import blinker
|
|
with suppress(Exception):
|
|
use_blinker_ver = constants.SeleniumWire.BLINKER_VER
|
|
if blinker.__version__ != use_blinker_ver:
|
|
shared_utils.pip_install(
|
|
"blinker", version=use_blinker_ver
|
|
)
|
|
del blinker
|
|
except Exception:
|
|
shared_utils.pip_install(
|
|
"blinker", version=constants.SeleniumWire.BLINKER_VER
|
|
)
|
|
shared_utils.pip_install(
|
|
"selenium-wire", version=constants.SeleniumWire.VER
|
|
)
|
|
from seleniumwire import webdriver
|
|
warnings.simplefilter("ignore", category=DeprecationWarning)
|
|
else:
|
|
from selenium import webdriver
|
|
|
|
# Construct the address for connecting to a Selenium Grid
|
|
if servername.startswith("https://"):
|
|
protocol = "https"
|
|
servername = servername.split("https://")[1]
|
|
elif "://" in servername:
|
|
servername = servername.split("://")[1]
|
|
server_with_port = ""
|
|
if ":" not in servername:
|
|
col_port = ":" + str(port)
|
|
first_slash = servername.find("/")
|
|
if first_slash != -1:
|
|
server_with_port = (
|
|
servername[:first_slash] + col_port + servername[first_slash:]
|
|
)
|
|
else:
|
|
server_with_port = servername + col_port
|
|
else:
|
|
server_with_port = servername
|
|
address = "%s://%s" % (protocol, server_with_port)
|
|
if not address.endswith("/wd/hub"):
|
|
if address.endswith("/"):
|
|
address += "wd/hub"
|
|
else:
|
|
address += "/wd/hub"
|
|
downloads_path = DOWNLOADS_FOLDER
|
|
desired_caps = {}
|
|
extra_caps = {}
|
|
if cap_file:
|
|
from seleniumbase.core import capabilities_parser
|
|
desired_caps = capabilities_parser.get_desired_capabilities(cap_file)
|
|
if cap_string:
|
|
import json
|
|
try:
|
|
extra_caps = json.loads(str(cap_string))
|
|
except Exception as e:
|
|
p1 = "Invalid input format for --cap-string:\n %s" % e
|
|
p2 = "The --cap-string input was: %s" % cap_string
|
|
p3 = (
|
|
"Enclose cap-string in SINGLE quotes; "
|
|
"keys and values in DOUBLE quotes."
|
|
)
|
|
p4 = (
|
|
"""Here's an example of correct cap-string usage:\n """
|
|
"""--cap-string='{"browserName":"chrome","name":"test1"}'"""
|
|
)
|
|
raise Exception("%s\n%s\n%s\n%s" % (p1, p2, p3, p4))
|
|
for cap_key in extra_caps.keys():
|
|
desired_caps[cap_key] = extra_caps[cap_key]
|
|
if cap_file or cap_string:
|
|
if "name" in desired_caps.keys():
|
|
if desired_caps["name"] == "*":
|
|
desired_caps["name"] = test_id
|
|
if browser_name == constants.Browser.GOOGLE_CHROME:
|
|
chrome_options = _set_chrome_options(
|
|
browser_name,
|
|
downloads_path,
|
|
headless,
|
|
locale_code,
|
|
proxy_string,
|
|
proxy_auth,
|
|
proxy_user,
|
|
proxy_pass,
|
|
proxy_bypass_list,
|
|
proxy_pac_url,
|
|
multi_proxy,
|
|
user_agent,
|
|
recorder_ext,
|
|
disable_cookies,
|
|
disable_js,
|
|
disable_csp,
|
|
enable_ws,
|
|
enable_sync,
|
|
use_auto_ext,
|
|
undetectable,
|
|
uc_cdp_events,
|
|
uc_subprocess,
|
|
log_cdp_events,
|
|
no_sandbox,
|
|
disable_gpu,
|
|
headless1,
|
|
headless2,
|
|
incognito,
|
|
guest_mode,
|
|
dark_mode,
|
|
devtools,
|
|
remote_debug,
|
|
enable_3d_apis,
|
|
swiftshader,
|
|
ad_block_on,
|
|
host_resolver_rules,
|
|
block_images,
|
|
do_not_track,
|
|
chromium_arg,
|
|
user_data_dir,
|
|
extension_zip,
|
|
extension_dir,
|
|
disable_features,
|
|
binary_location,
|
|
driver_version,
|
|
page_load_strategy,
|
|
use_wire,
|
|
external_pdf,
|
|
servername,
|
|
mobile_emulator,
|
|
device_width,
|
|
device_height,
|
|
device_pixel_ratio,
|
|
)
|
|
capabilities = webdriver.ChromeOptions().to_capabilities()
|
|
# Set custom desired capabilities
|
|
selenoid = False
|
|
selenoid_options = None
|
|
screen_resolution = None
|
|
browser_version = None
|
|
platform_name = None
|
|
extension_capabilities = {}
|
|
for key in desired_caps.keys():
|
|
capabilities[key] = desired_caps[key]
|
|
if key == "selenoid:options":
|
|
selenoid = True
|
|
selenoid_options = desired_caps[key]
|
|
elif key == "screenResolution":
|
|
screen_resolution = desired_caps[key]
|
|
elif key == "version" or key == "browserVersion":
|
|
browser_version = desired_caps[key]
|
|
elif key == "platform" or key == "platformName":
|
|
platform_name = desired_caps[key]
|
|
elif re.match("[a-zA-Z0-9]*:[a-zA-Z0-9]*", key):
|
|
extension_capabilities[key] = desired_caps[key]
|
|
cap_str = str(desired_caps).lower()
|
|
if "browserstack" in cap_str or "bstack" in cap_str:
|
|
chrome_options.set_capability("bstack:options", desired_caps)
|
|
chrome_options.set_capability("cloud:options", capabilities)
|
|
if selenoid:
|
|
snops = selenoid_options
|
|
chrome_options.set_capability("selenoid:options", snops)
|
|
if screen_resolution:
|
|
scres = screen_resolution
|
|
chrome_options.set_capability("screenResolution", scres)
|
|
if browser_version:
|
|
br_vers = browser_version
|
|
chrome_options.set_capability("browserVersion", br_vers)
|
|
if platform_name:
|
|
plat_name = platform_name
|
|
chrome_options.set_capability("platformName", plat_name)
|
|
if extension_capabilities:
|
|
for key in extension_capabilities:
|
|
ext_caps = extension_capabilities
|
|
chrome_options.set_capability(key, ext_caps[key])
|
|
driver = webdriver.Remote(
|
|
command_executor=address,
|
|
options=chrome_options,
|
|
)
|
|
return extend_driver(driver)
|
|
elif browser_name == constants.Browser.FIREFOX:
|
|
firefox_options = _set_firefox_options(
|
|
downloads_path,
|
|
headless,
|
|
locale_code,
|
|
proxy_string,
|
|
proxy_bypass_list,
|
|
proxy_pac_url,
|
|
user_agent,
|
|
disable_cookies,
|
|
disable_js,
|
|
disable_csp,
|
|
firefox_arg,
|
|
firefox_pref,
|
|
external_pdf,
|
|
)
|
|
capabilities = webdriver.FirefoxOptions().to_capabilities()
|
|
capabilities["marionette"] = True
|
|
if IS_LINUX and headless:
|
|
capabilities["moz:firefoxOptions"] = {"args": ["-headless"]}
|
|
# Set custom desired capabilities
|
|
selenoid = False
|
|
selenoid_options = None
|
|
screen_resolution = None
|
|
browser_version = None
|
|
platform_name = None
|
|
extension_capabilities = {}
|
|
for key in desired_caps.keys():
|
|
capabilities[key] = desired_caps[key]
|
|
if key == "selenoid:options":
|
|
selenoid = True
|
|
selenoid_options = desired_caps[key]
|
|
elif key == "screenResolution":
|
|
screen_resolution = desired_caps[key]
|
|
elif key == "version" or key == "browserVersion":
|
|
browser_version = desired_caps[key]
|
|
elif key == "platform" or key == "platformName":
|
|
platform_name = desired_caps[key]
|
|
elif re.match("[a-zA-Z0-9]*:[a-zA-Z0-9]*", key):
|
|
extension_capabilities[key] = desired_caps[key]
|
|
cap_str = str(desired_caps).lower()
|
|
if "browserstack" in cap_str or "bstack" in cap_str:
|
|
firefox_options.set_capability("bstack:options", desired_caps)
|
|
firefox_options.set_capability("cloud:options", capabilities)
|
|
if selenoid:
|
|
snops = selenoid_options
|
|
firefox_options.set_capability("selenoid:options", snops)
|
|
if screen_resolution:
|
|
scres = screen_resolution
|
|
firefox_options.set_capability("screenResolution", scres)
|
|
if browser_version:
|
|
br_vers = browser_version
|
|
firefox_options.set_capability("browserVersion", br_vers)
|
|
if platform_name:
|
|
plat_name = platform_name
|
|
firefox_options.set_capability("platformName", plat_name)
|
|
if extension_capabilities:
|
|
for key in extension_capabilities:
|
|
ext_caps = extension_capabilities
|
|
firefox_options.set_capability(key, ext_caps[key])
|
|
driver = webdriver.Remote(
|
|
command_executor=address,
|
|
options=firefox_options,
|
|
)
|
|
return extend_driver(driver)
|
|
elif browser_name == constants.Browser.INTERNET_EXPLORER:
|
|
capabilities = webdriver.DesiredCapabilities.INTERNETEXPLORER
|
|
remote_options = ArgOptions()
|
|
remote_options.set_capability("cloud:options", desired_caps)
|
|
driver = webdriver.Remote(
|
|
command_executor=address,
|
|
options=remote_options,
|
|
)
|
|
return extend_driver(driver)
|
|
elif browser_name == constants.Browser.EDGE:
|
|
edge_options = _set_chrome_options(
|
|
browser_name,
|
|
downloads_path,
|
|
headless,
|
|
locale_code,
|
|
proxy_string,
|
|
proxy_auth,
|
|
proxy_user,
|
|
proxy_pass,
|
|
proxy_bypass_list,
|
|
proxy_pac_url,
|
|
multi_proxy,
|
|
user_agent,
|
|
recorder_ext,
|
|
disable_cookies,
|
|
disable_js,
|
|
disable_csp,
|
|
enable_ws,
|
|
enable_sync,
|
|
use_auto_ext,
|
|
undetectable,
|
|
uc_cdp_events,
|
|
uc_subprocess,
|
|
log_cdp_events,
|
|
no_sandbox,
|
|
disable_gpu,
|
|
headless1,
|
|
headless2,
|
|
incognito,
|
|
guest_mode,
|
|
dark_mode,
|
|
devtools,
|
|
remote_debug,
|
|
enable_3d_apis,
|
|
swiftshader,
|
|
ad_block_on,
|
|
host_resolver_rules,
|
|
block_images,
|
|
do_not_track,
|
|
chromium_arg,
|
|
user_data_dir,
|
|
extension_zip,
|
|
extension_dir,
|
|
disable_features,
|
|
binary_location,
|
|
driver_version,
|
|
page_load_strategy,
|
|
use_wire,
|
|
external_pdf,
|
|
servername,
|
|
mobile_emulator,
|
|
device_width,
|
|
device_height,
|
|
device_pixel_ratio,
|
|
)
|
|
capabilities = webdriver.EdgeOptions().to_capabilities()
|
|
# Set custom desired capabilities
|
|
selenoid = False
|
|
selenoid_options = None
|
|
screen_resolution = None
|
|
browser_version = None
|
|
platform_name = None
|
|
extension_capabilities = {}
|
|
for key in desired_caps.keys():
|
|
capabilities[key] = desired_caps[key]
|
|
if key == "selenoid:options":
|
|
selenoid = True
|
|
selenoid_options = desired_caps[key]
|
|
elif key == "screenResolution":
|
|
screen_resolution = desired_caps[key]
|
|
elif key == "version" or key == "browserVersion":
|
|
browser_version = desired_caps[key]
|
|
elif key == "platform" or key == "platformName":
|
|
platform_name = desired_caps[key]
|
|
elif re.match("[a-zA-Z0-9]*:[a-zA-Z0-9]*", key):
|
|
extension_capabilities[key] = desired_caps[key]
|
|
edge_options.set_capability("cloud:options", capabilities)
|
|
if selenoid:
|
|
snops = selenoid_options
|
|
edge_options.set_capability("selenoid:options", snops)
|
|
if screen_resolution:
|
|
scres = screen_resolution
|
|
edge_options.set_capability("screenResolution", scres)
|
|
if browser_version:
|
|
br_vers = browser_version
|
|
edge_options.set_capability("browserVersion", br_vers)
|
|
if platform_name:
|
|
plat_name = platform_name
|
|
edge_options.set_capability("platformName", plat_name)
|
|
if extension_capabilities:
|
|
for key in extension_capabilities:
|
|
ext_caps = extension_capabilities
|
|
edge_options.set_capability(key, ext_caps[key])
|
|
driver = webdriver.Remote(
|
|
command_executor=address,
|
|
options=edge_options,
|
|
)
|
|
return extend_driver(driver)
|
|
elif browser_name == constants.Browser.SAFARI:
|
|
capabilities = webdriver.DesiredCapabilities.SAFARI
|
|
remote_options = ArgOptions()
|
|
remote_options.set_capability("cloud:options", desired_caps)
|
|
driver = webdriver.Remote(
|
|
command_executor=address,
|
|
options=remote_options,
|
|
)
|
|
return extend_driver(driver)
|
|
elif browser_name == constants.Browser.REMOTE:
|
|
remote_options = ArgOptions()
|
|
for cap_name, cap_value in desired_caps.items():
|
|
remote_options.set_capability(cap_name, cap_value)
|
|
cap_str = str(desired_caps).lower()
|
|
if "browserstack" in cap_str or "bstack" in cap_str:
|
|
remote_options.set_capability("bstack:options", desired_caps)
|
|
driver = webdriver.Remote(
|
|
command_executor=address,
|
|
options=remote_options,
|
|
)
|
|
return extend_driver(driver)
|
|
|
|
|
|
def get_local_driver(
|
|
browser_name,
|
|
headless,
|
|
locale_code,
|
|
servername,
|
|
proxy_string,
|
|
proxy_auth,
|
|
proxy_user,
|
|
proxy_pass,
|
|
proxy_bypass_list,
|
|
proxy_pac_url,
|
|
multi_proxy,
|
|
user_agent,
|
|
recorder_ext,
|
|
disable_cookies,
|
|
disable_js,
|
|
disable_csp,
|
|
enable_ws,
|
|
enable_sync,
|
|
use_auto_ext,
|
|
undetectable,
|
|
uc_cdp_events,
|
|
uc_subprocess,
|
|
log_cdp_events,
|
|
no_sandbox,
|
|
disable_gpu,
|
|
headless1,
|
|
headless2,
|
|
incognito,
|
|
guest_mode,
|
|
dark_mode,
|
|
devtools,
|
|
remote_debug,
|
|
enable_3d_apis,
|
|
swiftshader,
|
|
ad_block_on,
|
|
host_resolver_rules,
|
|
block_images,
|
|
do_not_track,
|
|
chromium_arg,
|
|
firefox_arg,
|
|
firefox_pref,
|
|
user_data_dir,
|
|
extension_zip,
|
|
extension_dir,
|
|
disable_features,
|
|
binary_location,
|
|
driver_version,
|
|
page_load_strategy,
|
|
use_wire,
|
|
external_pdf,
|
|
mobile_emulator,
|
|
device_width,
|
|
device_height,
|
|
device_pixel_ratio,
|
|
):
|
|
"""Spins up a new web browser and returns the driver.
|
|
Can also be used to spin up additional browsers for the same test."""
|
|
downloads_path = DOWNLOADS_FOLDER
|
|
b_path = binary_location
|
|
use_uc = is_using_uc(undetectable, browser_name)
|
|
if use_wire:
|
|
pip_find_lock = fasteners.InterProcessLock(
|
|
constants.PipInstall.FINDLOCK
|
|
)
|
|
with pip_find_lock: # Prevent issues with multiple processes
|
|
with suppress(Exception):
|
|
shared_utils.make_writable(constants.PipInstall.FINDLOCK)
|
|
try:
|
|
from seleniumwire import webdriver
|
|
import blinker
|
|
with suppress(Exception):
|
|
use_blinker_ver = constants.SeleniumWire.BLINKER_VER
|
|
if blinker.__version__ != use_blinker_ver:
|
|
shared_utils.pip_install(
|
|
"blinker", version=use_blinker_ver
|
|
)
|
|
del blinker
|
|
except Exception:
|
|
shared_utils.pip_install(
|
|
"blinker", version=constants.SeleniumWire.BLINKER_VER
|
|
)
|
|
shared_utils.pip_install(
|
|
"selenium-wire", version=constants.SeleniumWire.VER
|
|
)
|
|
from seleniumwire import webdriver
|
|
warnings.simplefilter("ignore", category=DeprecationWarning)
|
|
else:
|
|
from selenium import webdriver
|
|
|
|
if browser_name == constants.Browser.FIREFOX:
|
|
firefox_options = _set_firefox_options(
|
|
downloads_path,
|
|
headless,
|
|
locale_code,
|
|
proxy_string,
|
|
proxy_bypass_list,
|
|
proxy_pac_url,
|
|
user_agent,
|
|
disable_cookies,
|
|
disable_js,
|
|
disable_csp,
|
|
firefox_arg,
|
|
firefox_pref,
|
|
external_pdf,
|
|
)
|
|
if LOCAL_GECKODRIVER and os.path.exists(LOCAL_GECKODRIVER):
|
|
try:
|
|
make_driver_executable_if_not(LOCAL_GECKODRIVER)
|
|
except Exception as e:
|
|
logging.debug(
|
|
"\nWarning: Could not make geckodriver"
|
|
" executable: %s" % e
|
|
)
|
|
elif not geckodriver_on_path():
|
|
from seleniumbase.console_scripts import sb_install
|
|
args = " ".join(sys.argv)
|
|
if not ("-n" in sys.argv or " -n=" in args or args == "-c"):
|
|
# (Not multithreaded)
|
|
sys_args = sys.argv # Save a copy of current sys args
|
|
log_d("\nWarning: geckodriver not found. Getting it now:")
|
|
try:
|
|
sb_install.main(override="geckodriver")
|
|
except Exception as e:
|
|
log_d("\nWarning: Could not install geckodriver: %s" % e)
|
|
sys.argv = sys_args # Put back the original sys args
|
|
else:
|
|
geckodriver_fixing_lock = fasteners.InterProcessLock(
|
|
constants.MultiBrowser.DRIVER_FIXING_LOCK
|
|
)
|
|
with geckodriver_fixing_lock:
|
|
with suppress(Exception):
|
|
shared_utils.make_writable(
|
|
constants.MultiBrowser.DRIVER_FIXING_LOCK
|
|
)
|
|
if not geckodriver_on_path():
|
|
sys_args = sys.argv # Save a copy of sys args
|
|
log_d(
|
|
"\nWarning: geckodriver not found. "
|
|
"Getting it now:"
|
|
)
|
|
sb_install.main(override="geckodriver")
|
|
sys.argv = sys_args # Put back original sys args
|
|
# Launch Firefox
|
|
if os.path.exists(LOCAL_GECKODRIVER):
|
|
service = FirefoxService(
|
|
executable_path=LOCAL_GECKODRIVER,
|
|
log_output=os.devnull,
|
|
)
|
|
try:
|
|
driver = webdriver.Firefox(
|
|
service=service,
|
|
options=firefox_options,
|
|
)
|
|
return extend_driver(driver)
|
|
except BaseException as e:
|
|
if (
|
|
"geckodriver unexpectedly exited" in str(e)
|
|
or "Process unexpectedly closed" in str(e)
|
|
or "Failed to read marionette port" in str(e)
|
|
or "A connection attempt failed" in str(e)
|
|
or "Expected browser binary" in str(e)
|
|
or hasattr(e, "msg") and (
|
|
"geckodriver unexpectedly exited" in e.msg
|
|
or "Process unexpectedly closed" in e.msg
|
|
or "Failed to read marionette port" in e.msg
|
|
or "A connection attempt failed" in e.msg
|
|
or "Expected browser binary" in e.msg
|
|
)
|
|
):
|
|
time.sleep(0.1)
|
|
if (
|
|
IS_LINUX
|
|
and headless
|
|
and (
|
|
"unexpected" in str(e)
|
|
or (
|
|
hasattr(e, "msg") and "unexpected" in e.msg
|
|
)
|
|
)
|
|
):
|
|
firefox_options.add_argument("-headless")
|
|
driver = webdriver.Firefox(
|
|
service=service,
|
|
options=firefox_options,
|
|
)
|
|
return extend_driver(driver)
|
|
else:
|
|
raise # Not an obvious fix.
|
|
else:
|
|
service = FirefoxService(log_output=os.devnull)
|
|
try:
|
|
driver = webdriver.Firefox(
|
|
service=service,
|
|
options=firefox_options,
|
|
)
|
|
return extend_driver(driver)
|
|
except BaseException as e:
|
|
if (
|
|
"geckodriver unexpectedly exited" in str(e)
|
|
or "Process unexpectedly closed" in str(e)
|
|
or "Failed to read marionette port" in str(e)
|
|
or "A connection attempt failed" in str(e)
|
|
or "Expected browser binary" in str(e)
|
|
or hasattr(e, "msg") and (
|
|
"geckodriver unexpectedly exited" in e.msg
|
|
or "Process unexpectedly closed" in e.msg
|
|
or "Failed to read marionette port" in e.msg
|
|
or "A connection attempt failed" in e.msg
|
|
or "Expected browser binary" in e.msg
|
|
)
|
|
):
|
|
time.sleep(0.1)
|
|
if (
|
|
IS_LINUX
|
|
and headless
|
|
and (
|
|
"unexpected" in str(e)
|
|
or (
|
|
hasattr(e, "msg") and "unexpected" in e.msg
|
|
)
|
|
)
|
|
):
|
|
firefox_options.add_argument("-headless")
|
|
driver = webdriver.Firefox(
|
|
service=service,
|
|
options=firefox_options,
|
|
)
|
|
return extend_driver(driver)
|
|
else:
|
|
raise # Not an obvious fix.
|
|
elif browser_name == constants.Browser.INTERNET_EXPLORER:
|
|
if not IS_WINDOWS:
|
|
raise Exception(
|
|
"IE Browser is for Windows-based systems only!"
|
|
)
|
|
from selenium.webdriver.ie.options import Options
|
|
from selenium.webdriver.ie.service import Service
|
|
ie_options = Options()
|
|
ie_options.add_argument("--guest")
|
|
ie_options.attach_to_edge_chrome = True
|
|
ie_options.ignore_protected_mode_settings = True
|
|
ie_options.ignore_zoom_level = True
|
|
ie_options.require_window_focus = False
|
|
ie_options.native_events = True
|
|
ie_options.full_page_screenshot = True
|
|
ie_options.persistent_hover = True
|
|
if LOCAL_IEDRIVER and os.path.exists(LOCAL_IEDRIVER):
|
|
try:
|
|
make_driver_executable_if_not(LOCAL_IEDRIVER)
|
|
except Exception as e:
|
|
logging.debug(
|
|
"\nWarning: Could not make IEDriver executable: %s" % e
|
|
)
|
|
elif not iedriver_on_path():
|
|
from seleniumbase.console_scripts import sb_install
|
|
args = " ".join(sys.argv)
|
|
if not ("-n" in sys.argv or " -n=" in args or args == "-c"):
|
|
# (Not multithreaded)
|
|
sys_args = sys.argv # Save a copy of current sys args
|
|
log_d("\nWarning: IEDriver not found. Getting it now:")
|
|
sb_install.main(override="iedriver")
|
|
sys.argv = sys_args # Put back the original sys args
|
|
if LOCAL_HEADLESS_IEDRIVER and os.path.exists(LOCAL_HEADLESS_IEDRIVER):
|
|
try:
|
|
make_driver_executable_if_not(LOCAL_HEADLESS_IEDRIVER)
|
|
except Exception as e:
|
|
logging.debug(
|
|
"\nWarning: Could not make HeadlessIEDriver executable: %s"
|
|
% e
|
|
)
|
|
elif not headless_iedriver_on_path():
|
|
from seleniumbase.console_scripts import sb_install
|
|
args = " ".join(sys.argv)
|
|
if not ("-n" in sys.argv or " -n=" in args or args == "-c"):
|
|
# (Not multithreaded)
|
|
sys_args = sys.argv # Save a copy of current sys args
|
|
log_d("\nWarning: HeadlessIEDriver not found. Getting it now:")
|
|
sb_install.main(override="iedriver")
|
|
sys.argv = sys_args # Put back the original sys args
|
|
d_b_c = "--disable-build-check"
|
|
logger = logging.getLogger("selenium")
|
|
logger.setLevel("INFO")
|
|
if not headless:
|
|
warnings.simplefilter("ignore", category=DeprecationWarning)
|
|
service = Service(service_args=[d_b_c], log_output=os.devnull)
|
|
driver = webdriver.Ie(service=service, options=ie_options)
|
|
return extend_driver(driver)
|
|
else:
|
|
warnings.simplefilter("ignore", category=DeprecationWarning)
|
|
service = Service(
|
|
executable_path=LOCAL_IEDRIVER,
|
|
service_args=[d_b_c],
|
|
log_output=os.devnull,
|
|
)
|
|
driver = webdriver.Ie(service=service, options=ie_options)
|
|
return extend_driver(driver)
|
|
elif browser_name == constants.Browser.EDGE:
|
|
prefs = {
|
|
"download.default_directory": downloads_path,
|
|
"download.directory_upgrade": True,
|
|
"download.prompt_for_download": False,
|
|
"credentials_enable_service": False,
|
|
"local_discovery.notifications_enabled": False,
|
|
"safebrowsing.disable_download_protection": True,
|
|
"safebrowsing.enabled": False, # Prevent PW "data breach" pop-ups
|
|
"omnibox-max-zero-suggest-matches": 0,
|
|
"omnibox-use-existing-autocomplete-client": 0,
|
|
"omnibox-trending-zero-prefix-suggestions-on-ntp": 0,
|
|
"omnibox-local-history-zero-suggest-beyond-ntp": 0,
|
|
"omnibox-on-focus-suggestions-contextual-web": 0,
|
|
"omnibox-on-focus-suggestions-srp": 0,
|
|
"omnibox-zero-suggest-prefetching": 0,
|
|
"omnibox-zero-suggest-prefetching-on-srp": 0,
|
|
"omnibox-zero-suggest-prefetching-on-web": 0,
|
|
"omnibox-zero-suggest-in-memory-caching": 0,
|
|
"content_settings.exceptions.automatic_downloads.*.setting": 1,
|
|
"default_content_setting_values.notifications": 0,
|
|
"default_content_settings.popups": 0,
|
|
"managed_default_content_settings.popups": 0,
|
|
"profile.password_manager_enabled": False,
|
|
"profile.password_manager_leak_detection": False,
|
|
"profile.default_content_setting_values.notifications": 2,
|
|
"profile.default_content_settings.popups": 0,
|
|
"profile.managed_default_content_settings.popups": 0,
|
|
"profile.default_content_setting_values.automatic_downloads": 1,
|
|
}
|
|
use_version = "latest"
|
|
major_edge_version = None
|
|
saved_mev = None
|
|
use_br_version_for_edge = False
|
|
use_exact_version_for_edge = False
|
|
try:
|
|
if binary_location:
|
|
try:
|
|
major_edge_version = (
|
|
detect_b_ver.get_browser_version_from_binary(
|
|
binary_location
|
|
)
|
|
)
|
|
saved_mev = major_edge_version
|
|
major_edge_version = saved_mev.split(".")[0]
|
|
if len(major_edge_version) < 2:
|
|
major_edge_version = None
|
|
except Exception:
|
|
major_edge_version = None
|
|
if not major_edge_version:
|
|
br_app = "edge"
|
|
major_edge_version = (
|
|
detect_b_ver.get_browser_version_from_os(br_app)
|
|
)
|
|
saved_mev = major_edge_version
|
|
major_edge_version = major_edge_version.split(".")[0]
|
|
if int(major_edge_version) < 80:
|
|
major_edge_version = None
|
|
elif int(major_edge_version) >= 115:
|
|
if (
|
|
driver_version == "browser"
|
|
and saved_mev
|
|
and len(saved_mev.split(".")) == 4
|
|
):
|
|
driver_version = saved_mev
|
|
use_br_version_for_edge = True
|
|
except Exception:
|
|
major_edge_version = None
|
|
if driver_version and "." in driver_version:
|
|
use_exact_version_for_edge = True
|
|
if use_br_version_for_edge:
|
|
major_edge_version = saved_mev
|
|
if major_edge_version:
|
|
use_version = major_edge_version
|
|
edge_driver_version = None
|
|
edgedriver_upgrade_needed = False
|
|
if os.path.exists(LOCAL_EDGEDRIVER):
|
|
with suppress(Exception):
|
|
output = subprocess.check_output(
|
|
'"%s" --version' % LOCAL_EDGEDRIVER, shell=True
|
|
)
|
|
if IS_WINDOWS:
|
|
output = output.decode("latin1")
|
|
else:
|
|
output = output.decode("utf-8")
|
|
if output.split(" ")[0] == "MSEdgeDriver":
|
|
# MSEdgeDriver VERSION
|
|
output = output.split(" ")[1]
|
|
if use_exact_version_for_edge:
|
|
edge_driver_version = output.split(" ")[0]
|
|
output = output.split(".")[0]
|
|
elif output.split(" ")[0] == "Microsoft":
|
|
output = output.split(" ")[3]
|
|
if use_exact_version_for_edge:
|
|
edge_driver_version = output.split(" ")[0]
|
|
output = output.split(".")[0]
|
|
else:
|
|
output = 0
|
|
if int(output) >= 2:
|
|
if not use_exact_version_for_edge:
|
|
edge_driver_version = output
|
|
if driver_version == "keep":
|
|
driver_version = edge_driver_version
|
|
use_version = find_edgedriver_version_to_use(
|
|
use_version, driver_version
|
|
)
|
|
local_edgedriver_exists = False
|
|
if LOCAL_EDGEDRIVER and os.path.exists(LOCAL_EDGEDRIVER):
|
|
local_edgedriver_exists = True
|
|
if (
|
|
use_version != "latest"
|
|
and edge_driver_version
|
|
and use_version != edge_driver_version
|
|
):
|
|
edgedriver_upgrade_needed = True
|
|
else:
|
|
try:
|
|
make_driver_executable_if_not(LOCAL_EDGEDRIVER)
|
|
except Exception as e:
|
|
logging.debug(
|
|
"\nWarning: Could not make edgedriver"
|
|
" executable: %s" % e
|
|
)
|
|
if not local_edgedriver_exists or edgedriver_upgrade_needed:
|
|
from seleniumbase.console_scripts import sb_install
|
|
args = " ".join(sys.argv)
|
|
if not ("-n" in sys.argv or " -n=" in args or args == "-c"):
|
|
# (Not multithreaded)
|
|
msg = "Microsoft Edge Driver not found."
|
|
if edgedriver_upgrade_needed:
|
|
msg = "Microsoft Edge Driver update needed."
|
|
sys_args = sys.argv # Save a copy of current sys args
|
|
log_d("\n%s Getting it now:" % msg)
|
|
sb_install.main(override="edgedriver %s" % use_version)
|
|
sys.argv = sys_args # Put back the original sys args
|
|
else:
|
|
edgedriver_fixing_lock = fasteners.InterProcessLock(
|
|
constants.MultiBrowser.DRIVER_FIXING_LOCK
|
|
)
|
|
with edgedriver_fixing_lock:
|
|
with suppress(Exception):
|
|
shared_utils.make_writable(
|
|
constants.MultiBrowser.DRIVER_FIXING_LOCK
|
|
)
|
|
msg = "Microsoft Edge Driver not found."
|
|
if edgedriver_upgrade_needed:
|
|
msg = "Microsoft Edge Driver update needed."
|
|
sys_args = sys.argv # Save a copy of current sys args
|
|
log_d("\n%s Getting it now:" % msg)
|
|
sb_install.main(override="edgedriver %s" % use_version)
|
|
sys.argv = sys_args # Put back the original sys args
|
|
|
|
# For Microsoft Edge (Chromium) version 80 or higher
|
|
Edge = webdriver.edge.webdriver.WebDriver
|
|
EdgeOptions = webdriver.edge.webdriver.Options
|
|
if LOCAL_EDGEDRIVER and os.path.exists(LOCAL_EDGEDRIVER):
|
|
try:
|
|
make_driver_executable_if_not(LOCAL_EDGEDRIVER)
|
|
except Exception as e:
|
|
logging.debug(
|
|
"\nWarning: Could not make edgedriver"
|
|
" executable: %s" % e
|
|
)
|
|
edge_options = EdgeOptions()
|
|
edge_options.use_chromium = True
|
|
if locale_code:
|
|
prefs["intl.accept_languages"] = locale_code
|
|
if block_images:
|
|
prefs["profile.managed_default_content_settings.images"] = 2
|
|
if disable_cookies:
|
|
prefs["profile.default_content_setting_values.cookies"] = 2
|
|
if disable_js:
|
|
prefs["profile.managed_default_content_settings.javascript"] = 2
|
|
if do_not_track:
|
|
prefs["enable_do_not_track"] = True
|
|
if external_pdf:
|
|
prefs["plugins.always_open_pdf_externally"] = True
|
|
pdce = "user_experience_metrics.personalization_data_consent_enabled"
|
|
prefs[pdce] = True # Remove "Personalize your web experience" prompt
|
|
edge_options.add_experimental_option("prefs", prefs)
|
|
edge_options.add_argument(
|
|
"--disable-blink-features=AutomationControlled"
|
|
)
|
|
edge_options.add_experimental_option(
|
|
"excludeSwitches", ["enable-automation", "enable-logging"]
|
|
)
|
|
if log_cdp_events:
|
|
edge_options.set_capability(
|
|
"ms:loggingPrefs", {"performance": "ALL", "browser": "ALL"}
|
|
)
|
|
if host_resolver_rules:
|
|
edge_options.add_argument(
|
|
"--host-resolver-rules=%s" % host_resolver_rules
|
|
)
|
|
if not enable_sync:
|
|
edge_options.add_argument("--disable-sync")
|
|
if (
|
|
not recorder_ext and not disable_csp and not proxy_auth
|
|
):
|
|
edge_options.add_argument("--guest")
|
|
if dark_mode:
|
|
edge_options.add_argument("--enable-features=WebContentsForceDark")
|
|
if headless1:
|
|
# developer.chrome.com/blog/removing-headless-old-from-chrome
|
|
with suppress(Exception):
|
|
if int(str(use_version).split(".")[0]) >= 132:
|
|
headless1 = False
|
|
headless2 = True
|
|
if headless2:
|
|
try:
|
|
if use_version == "latest" or int(use_version) >= 109:
|
|
edge_options.add_argument("--headless=new")
|
|
else:
|
|
edge_options.add_argument("--headless=chrome")
|
|
except Exception:
|
|
edge_options.add_argument("--headless=new")
|
|
elif headless and undetectable:
|
|
# (For later: UC Mode doesn't support Edge now)
|
|
with suppress(Exception):
|
|
if int(use_version) >= 109:
|
|
edge_options.add_argument("--headless=new")
|
|
elif (
|
|
int(use_version) >= 96
|
|
and int(use_version) <= 108
|
|
):
|
|
edge_options.add_argument("--headless=chrome")
|
|
else:
|
|
pass # Will need Xvfb on Linux
|
|
elif headless:
|
|
if (
|
|
"--headless" not in edge_options.arguments
|
|
and "--headless=old" not in edge_options.arguments
|
|
):
|
|
if headless1:
|
|
edge_options.add_argument("--headless=old")
|
|
else:
|
|
edge_options.add_argument("--headless")
|
|
if mobile_emulator and not use_uc:
|
|
emulator_settings = {}
|
|
device_metrics = {}
|
|
if (
|
|
isinstance(device_width, int)
|
|
and isinstance(device_height, int)
|
|
and isinstance(device_pixel_ratio, (int, float))
|
|
):
|
|
device_metrics["width"] = device_width
|
|
device_metrics["height"] = device_height
|
|
device_metrics["pixelRatio"] = device_pixel_ratio
|
|
else:
|
|
device_metrics["width"] = constants.Mobile.WIDTH
|
|
device_metrics["height"] = constants.Mobile.HEIGHT
|
|
device_metrics["pixelRatio"] = constants.Mobile.RATIO
|
|
emulator_settings["deviceMetrics"] = device_metrics
|
|
if user_agent:
|
|
emulator_settings["userAgent"] = user_agent
|
|
edge_options.add_experimental_option(
|
|
"mobileEmulation", emulator_settings
|
|
)
|
|
# Handle Window Position
|
|
if (headless or headless2) and IS_WINDOWS:
|
|
# https://stackoverflow.com/a/78999088/7058266
|
|
edge_options.add_argument("--window-position=-2400,-2400")
|
|
else:
|
|
if (
|
|
hasattr(settings, "WINDOW_START_X")
|
|
and isinstance(settings.WINDOW_START_X, int)
|
|
and hasattr(settings, "WINDOW_START_Y")
|
|
and isinstance(settings.WINDOW_START_Y, int)
|
|
):
|
|
edge_options.add_argument(
|
|
"--window-position=%s,%s" % (
|
|
settings.WINDOW_START_X, settings.WINDOW_START_Y
|
|
)
|
|
)
|
|
# Handle Window Size
|
|
if headless or headless2:
|
|
if (
|
|
hasattr(settings, "HEADLESS_START_WIDTH")
|
|
and isinstance(settings.HEADLESS_START_WIDTH, int)
|
|
and hasattr(settings, "HEADLESS_START_HEIGHT")
|
|
and isinstance(settings.HEADLESS_START_HEIGHT, int)
|
|
):
|
|
edge_options.add_argument(
|
|
"--window-size=%s,%s" % (
|
|
settings.HEADLESS_START_WIDTH,
|
|
settings.HEADLESS_START_HEIGHT,
|
|
)
|
|
)
|
|
else:
|
|
if (
|
|
hasattr(settings, "CHROME_START_WIDTH")
|
|
and isinstance(settings.CHROME_START_WIDTH, int)
|
|
and hasattr(settings, "CHROME_START_HEIGHT")
|
|
and isinstance(settings.CHROME_START_HEIGHT, int)
|
|
):
|
|
edge_options.add_argument(
|
|
"--window-size=%s,%s" % (
|
|
settings.CHROME_START_WIDTH,
|
|
settings.CHROME_START_HEIGHT,
|
|
)
|
|
)
|
|
if user_data_dir and not use_uc:
|
|
abs_path = os.path.abspath(user_data_dir)
|
|
edge_options.add_argument("--user-data-dir=%s" % abs_path)
|
|
if extension_zip:
|
|
# Can be a comma-separated list of .ZIP or .CRX files
|
|
extension_zip_list = extension_zip.split(",")
|
|
for extension_zip_item in extension_zip_list:
|
|
abs_path = os.path.abspath(extension_zip_item)
|
|
edge_options.add_extension(abs_path)
|
|
if extension_dir:
|
|
# load-extension input can be a comma-separated list
|
|
abs_path = (
|
|
",".join(os.path.abspath(p) for p in extension_dir.split(","))
|
|
)
|
|
edge_options = add_chrome_ext_dir(edge_options, abs_path)
|
|
edge_options.add_argument("--disable-infobars")
|
|
edge_options.add_argument("--disable-notifications")
|
|
edge_options.add_argument("--disable-save-password-bubble")
|
|
edge_options.add_argument("--disable-single-click-autofill")
|
|
edge_options.add_argument(
|
|
"--disable-autofill-keyboard-accessory-view[8]"
|
|
)
|
|
edge_options.add_argument("--safebrowsing-disable-download-protection")
|
|
edge_options.add_argument("--disable-search-engine-choice-screen")
|
|
edge_options.add_argument("--disable-browser-side-navigation")
|
|
edge_options.add_argument("--disable-translate")
|
|
if not enable_ws:
|
|
edge_options.add_argument("--disable-web-security")
|
|
edge_options.add_argument("--homepage=about:blank")
|
|
edge_options.add_argument("--dns-prefetch-disable")
|
|
edge_options.add_argument("--dom-automation")
|
|
edge_options.add_argument("--disable-hang-monitor")
|
|
edge_options.add_argument("--disable-prompt-on-repost")
|
|
if not enable_3d_apis:
|
|
edge_options.add_argument("--disable-3d-apis")
|
|
if headless or headless2 or use_uc:
|
|
edge_options.add_argument("--disable-renderer-backgrounding")
|
|
edge_options.add_argument("--disable-backgrounding-occluded-windows")
|
|
edge_options.add_argument("--disable-client-side-phishing-detection")
|
|
edge_options.add_argument("--disable-oopr-debug-crash-dump")
|
|
edge_options.add_argument("--disable-top-sites")
|
|
edge_options.add_argument("--ash-no-nudges")
|
|
edge_options.add_argument("--no-crash-upload")
|
|
edge_options.add_argument("--deny-permission-prompts")
|
|
if (
|
|
page_load_strategy
|
|
and page_load_strategy.lower() in ["eager", "none"]
|
|
):
|
|
# Only change it if not "normal", which is the default.
|
|
edge_options.page_load_strategy = page_load_strategy.lower()
|
|
elif (
|
|
not page_load_strategy
|
|
and hasattr(settings, "PAGE_LOAD_STRATEGY")
|
|
and settings.PAGE_LOAD_STRATEGY
|
|
and settings.PAGE_LOAD_STRATEGY.lower() in ["eager", "none"]
|
|
):
|
|
# Only change it if not "normal", which is the default.
|
|
edge_options.page_load_strategy = (
|
|
settings.PAGE_LOAD_STRATEGY.lower()
|
|
)
|
|
if (settings.DISABLE_CSP_ON_CHROME or disable_csp) and not headless:
|
|
# Headless Edge doesn't support extensions, which are required
|
|
# for disabling the Content Security Policy on Edge
|
|
edge_options = _add_chrome_disable_csp_extension(edge_options)
|
|
if ad_block_on and not headless:
|
|
edge_options = _add_chrome_ad_block_extension(edge_options)
|
|
if recorder_ext and not headless:
|
|
edge_options = _add_chrome_recorder_extension(edge_options)
|
|
if proxy_string:
|
|
if proxy_auth:
|
|
edge_options = _add_chrome_proxy_extension(
|
|
edge_options,
|
|
proxy_string,
|
|
proxy_user,
|
|
proxy_pass,
|
|
proxy_bypass_list,
|
|
zip_it=True,
|
|
multi_proxy=multi_proxy,
|
|
)
|
|
edge_options.add_argument("--proxy-server=%s" % proxy_string)
|
|
if proxy_bypass_list:
|
|
edge_options.add_argument(
|
|
"--proxy-bypass-list=%s" % proxy_bypass_list
|
|
)
|
|
elif proxy_pac_url:
|
|
if proxy_auth:
|
|
edge_options = _add_chrome_proxy_extension(
|
|
edge_options,
|
|
None,
|
|
proxy_user,
|
|
proxy_pass,
|
|
proxy_bypass_list,
|
|
zip_it=True,
|
|
multi_proxy=multi_proxy,
|
|
)
|
|
edge_options.add_argument("--proxy-pac-url=%s" % proxy_pac_url)
|
|
edge_options.add_argument("--test-type")
|
|
edge_options.add_argument("--log-level=3")
|
|
edge_options.add_argument("--no-first-run")
|
|
edge_options.add_argument("--ignore-certificate-errors")
|
|
edge_options.add_argument("--ignore-ssl-errors=yes")
|
|
if devtools and not headless:
|
|
edge_options.add_argument("--auto-open-devtools-for-tabs")
|
|
edge_options.add_argument("--allow-file-access-from-files")
|
|
edge_options.add_argument("--allow-insecure-localhost")
|
|
edge_options.add_argument("--allow-running-insecure-content")
|
|
if user_agent:
|
|
edge_options.add_argument("--user-agent=%s" % user_agent)
|
|
if IS_LINUX or (IS_MAC and not use_uc):
|
|
edge_options.add_argument("--no-sandbox")
|
|
if remote_debug:
|
|
# To access the Debugger, go to: edge://inspect/#devices
|
|
# while a Chromium driver is running.
|
|
# Info: https://chromedevtools.github.io/devtools-protocol/
|
|
args = " ".join(sys.argv)
|
|
free_port = 9222
|
|
if ("-n" in sys.argv or " -n=" in args or args == "-c"):
|
|
free_port = service_utils.free_port()
|
|
edge_options.add_argument("--remote-debugging-port=%s" % free_port)
|
|
if swiftshader:
|
|
edge_options.add_argument("--use-gl=angle")
|
|
edge_options.add_argument("--use-angle=swiftshader-webgl")
|
|
elif not use_uc and not enable_3d_apis:
|
|
edge_options.add_argument("--disable-gpu")
|
|
if IS_LINUX:
|
|
edge_options.add_argument("--disable-dev-shm-usage")
|
|
extra_disabled_features = []
|
|
set_binary = False
|
|
if chromium_arg:
|
|
# Can be a comma-separated list of Chromium args
|
|
chromium_arg_list = None
|
|
if isinstance(chromium_arg, (list, tuple)):
|
|
chromium_arg_list = chromium_arg
|
|
else:
|
|
chromium_arg_list = chromium_arg.split(",")
|
|
for chromium_arg_item in chromium_arg_list:
|
|
chromium_arg_item = chromium_arg_item.strip()
|
|
if not chromium_arg_item.startswith("--"):
|
|
if chromium_arg_item.startswith("-"):
|
|
chromium_arg_item = "-" + chromium_arg_item
|
|
else:
|
|
chromium_arg_item = "--" + chromium_arg_item
|
|
if "set-binary" in chromium_arg_item:
|
|
set_binary = True
|
|
elif "disable-features=" in chromium_arg_item:
|
|
d_f = chromium_arg_item.split("disable-features=")[-1]
|
|
extra_disabled_features.append(d_f)
|
|
elif len(chromium_arg_item) >= 3:
|
|
edge_options.add_argument(chromium_arg_item)
|
|
if disable_features:
|
|
extra_disabled_features.extend(disable_features.split(","))
|
|
edge_options.add_argument(
|
|
'--simulate-outdated-no-au="Tue, 31 Dec 2099 23:59:59 GMT"'
|
|
)
|
|
edge_options.add_argument("--disable-ipc-flooding-protection")
|
|
edge_options.add_argument("--disable-password-generation")
|
|
edge_options.add_argument("--disable-domain-reliability")
|
|
edge_options.add_argument("--disable-breakpad")
|
|
included_disabled_features = []
|
|
included_disabled_features.append("OptimizationHints")
|
|
included_disabled_features.append("OptimizationHintsFetching")
|
|
included_disabled_features.append("Translate")
|
|
included_disabled_features.append("OptimizationTargetPrediction")
|
|
included_disabled_features.append("OptimizationGuideModelDownloading")
|
|
included_disabled_features.append("InsecureDownloadWarnings")
|
|
included_disabled_features.append("InterestFeedContentSuggestions")
|
|
included_disabled_features.append("PrivacySandboxSettings4")
|
|
included_disabled_features.append("SidePanelPinning")
|
|
included_disabled_features.append("UserAgentClientHint")
|
|
for item in extra_disabled_features:
|
|
if item not in included_disabled_features:
|
|
included_disabled_features.append(item)
|
|
d_f_string = ",".join(included_disabled_features)
|
|
edge_options.add_argument("--disable-features=%s" % d_f_string)
|
|
if (set_binary or IS_LINUX) and not binary_location:
|
|
br_app = "edge"
|
|
binary_loc = detect_b_ver.get_binary_location(br_app)
|
|
if os.path.exists(binary_loc):
|
|
binary_location = binary_loc
|
|
if binary_location:
|
|
edge_options.binary_location = binary_location
|
|
service = EdgeService(
|
|
executable_path=LOCAL_EDGEDRIVER,
|
|
log_output=os.devnull,
|
|
service_args=["--disable-build-check"],
|
|
)
|
|
try:
|
|
driver = Edge(service=service, options=edge_options)
|
|
except Exception as e:
|
|
if not hasattr(e, "msg"):
|
|
raise
|
|
auto_upgrade_edgedriver = False
|
|
edge_version = None
|
|
if (
|
|
"This version of MSEdgeDriver only supports" in e.msg
|
|
or "This version of Microsoft Edge WebDriver" in e.msg
|
|
):
|
|
if "Current browser version is " in e.msg:
|
|
auto_upgrade_edgedriver = True
|
|
edge_version = e.msg.split(
|
|
"Current browser version is "
|
|
)[1].split(" ")[0]
|
|
elif "only supports MSEdge version " in e.msg:
|
|
auto_upgrade_edgedriver = True
|
|
edge_version = e.msg.split(
|
|
"only supports MSEdge version "
|
|
)[1].split(" ")[0]
|
|
elif "DevToolsActivePort file doesn't exist" in e.msg:
|
|
# https://stackoverflow.com/a/56638103/7058266
|
|
args = " ".join(sys.argv)
|
|
free_port = 9222
|
|
if ("-n" in sys.argv or " -n=" in args or args == "-c"):
|
|
free_port = service_utils.free_port()
|
|
edge_options.add_argument(
|
|
"--remote-debugging-port=%s" % free_port
|
|
)
|
|
driver = Edge(service=service, options=edge_options)
|
|
return extend_driver(driver)
|
|
if not auto_upgrade_edgedriver:
|
|
raise # Not an obvious fix.
|
|
else:
|
|
pass # Try upgrading EdgeDriver to match Edge.
|
|
args = " ".join(sys.argv)
|
|
if "-n" in sys.argv or " -n=" in args or args == "-c":
|
|
edgedriver_fixing_lock = fasteners.InterProcessLock(
|
|
constants.MultiBrowser.DRIVER_FIXING_LOCK
|
|
)
|
|
with edgedriver_fixing_lock:
|
|
with suppress(Exception):
|
|
shared_utils.make_writable(
|
|
constants.MultiBrowser.DRIVER_FIXING_LOCK
|
|
)
|
|
with suppress(Exception):
|
|
if not _was_driver_repaired():
|
|
_repair_edgedriver(edge_version)
|
|
_mark_driver_repaired()
|
|
else:
|
|
with suppress(Exception):
|
|
if not _was_driver_repaired():
|
|
_repair_edgedriver(edge_version)
|
|
_mark_driver_repaired()
|
|
driver = Edge(service=service, options=edge_options)
|
|
return extend_driver(driver)
|
|
elif browser_name == constants.Browser.SAFARI:
|
|
args = " ".join(sys.argv)
|
|
if ("-n" in sys.argv or " -n=" in args or args == "-c"):
|
|
# Skip if multithreaded
|
|
raise Exception("Can't run Safari tests in multithreaded mode!")
|
|
warnings.simplefilter("ignore", category=DeprecationWarning)
|
|
from selenium.webdriver.safari.options import Options as SafariOptions
|
|
service = SafariService(quiet=False)
|
|
options = SafariOptions()
|
|
if (
|
|
page_load_strategy
|
|
and page_load_strategy.lower() in ["eager", "none"]
|
|
):
|
|
# Only change it if not "normal", which is the default.
|
|
options.page_load_strategy = page_load_strategy.lower()
|
|
elif (
|
|
not page_load_strategy
|
|
and hasattr(settings, "PAGE_LOAD_STRATEGY")
|
|
and settings.PAGE_LOAD_STRATEGY
|
|
and settings.PAGE_LOAD_STRATEGY.lower() in ["eager", "none"]
|
|
):
|
|
# Only change it if not "normal", which is the default.
|
|
options.page_load_strategy = settings.PAGE_LOAD_STRATEGY.lower()
|
|
driver = webdriver.safari.webdriver.WebDriver(
|
|
service=service, options=options
|
|
)
|
|
return extend_driver(driver)
|
|
elif browser_name == constants.Browser.GOOGLE_CHROME:
|
|
try:
|
|
chrome_options = _set_chrome_options(
|
|
browser_name,
|
|
downloads_path,
|
|
headless,
|
|
locale_code,
|
|
proxy_string,
|
|
proxy_auth,
|
|
proxy_user,
|
|
proxy_pass,
|
|
proxy_bypass_list,
|
|
proxy_pac_url,
|
|
multi_proxy,
|
|
user_agent,
|
|
recorder_ext,
|
|
disable_cookies,
|
|
disable_js,
|
|
disable_csp,
|
|
enable_ws,
|
|
enable_sync,
|
|
use_auto_ext,
|
|
undetectable,
|
|
uc_cdp_events,
|
|
uc_subprocess,
|
|
log_cdp_events,
|
|
no_sandbox,
|
|
disable_gpu,
|
|
headless1,
|
|
headless2,
|
|
incognito,
|
|
guest_mode,
|
|
dark_mode,
|
|
devtools,
|
|
remote_debug,
|
|
enable_3d_apis,
|
|
swiftshader,
|
|
ad_block_on,
|
|
host_resolver_rules,
|
|
block_images,
|
|
do_not_track,
|
|
chromium_arg,
|
|
user_data_dir,
|
|
extension_zip,
|
|
extension_dir,
|
|
disable_features,
|
|
binary_location,
|
|
driver_version,
|
|
page_load_strategy,
|
|
use_wire,
|
|
external_pdf,
|
|
servername,
|
|
mobile_emulator,
|
|
device_width,
|
|
device_height,
|
|
device_pixel_ratio,
|
|
)
|
|
use_version = "latest"
|
|
major_chrome_version = None
|
|
saved_mcv = None
|
|
full_ch_version = None
|
|
full_ch_driver_version = None
|
|
use_br_version_for_uc = False
|
|
try:
|
|
if chrome_options.binary_location:
|
|
try:
|
|
major_chrome_version = (
|
|
detect_b_ver.get_browser_version_from_binary(
|
|
chrome_options.binary_location,
|
|
)
|
|
)
|
|
saved_mcv = major_chrome_version
|
|
major_chrome_version = saved_mcv.split(".")[0]
|
|
if len(major_chrome_version) < 2:
|
|
major_chrome_version = None
|
|
except Exception:
|
|
major_chrome_version = None
|
|
if not major_chrome_version:
|
|
br_app = "google-chrome"
|
|
full_ch_version = (
|
|
detect_b_ver.get_browser_version_from_os(br_app)
|
|
)
|
|
saved_mcv = full_ch_version
|
|
major_chrome_version = full_ch_version.split(".")[0]
|
|
if int(major_chrome_version) < 67:
|
|
major_chrome_version = None
|
|
elif (
|
|
int(major_chrome_version) >= 67
|
|
and int(major_chrome_version) <= 72
|
|
):
|
|
# chromedrivers 2.41 - 2.46 could be swapped with 72
|
|
major_chrome_version = "72"
|
|
elif int(major_chrome_version) >= 115:
|
|
if (
|
|
driver_version == "browser"
|
|
and saved_mcv
|
|
and len(saved_mcv.split(".")) == 4
|
|
):
|
|
driver_version = saved_mcv
|
|
if use_uc:
|
|
use_br_version_for_uc = True
|
|
if (
|
|
(headless or headless2)
|
|
and IS_WINDOWS
|
|
and major_chrome_version
|
|
and int(major_chrome_version) >= 117
|
|
and not use_uc
|
|
and not (remote_debug or devtools or use_wire)
|
|
and not (proxy_string or multi_proxy or proxy_pac_url)
|
|
and (not chromium_arg or "debug" not in chromium_arg)
|
|
and (not servername or servername == "localhost")
|
|
):
|
|
# Hide the "DevTools listening on ..." message.
|
|
# https://bugs.chromium.org
|
|
# /p/chromedriver/issues/detail?id=4403#c35
|
|
# (Only when the remote debugging port is not needed.)
|
|
chrome_options.add_argument("--remote-debugging-pipe")
|
|
except Exception:
|
|
major_chrome_version = None
|
|
if major_chrome_version:
|
|
use_version = major_chrome_version
|
|
ch_driver_version = None
|
|
path_chromedriver = chromedriver_on_path()
|
|
if os.path.exists(LOCAL_CHROMEDRIVER):
|
|
with suppress(Exception):
|
|
output = subprocess.check_output(
|
|
'"%s" --version' % LOCAL_CHROMEDRIVER, shell=True
|
|
)
|
|
if IS_WINDOWS:
|
|
output = output.decode("latin1")
|
|
else:
|
|
output = output.decode("utf-8")
|
|
full_ch_driver_version = output.split(" ")[1]
|
|
output = full_ch_driver_version.split(".")[0]
|
|
if int(output) >= 2:
|
|
ch_driver_version = output
|
|
if driver_version == "keep":
|
|
driver_version = ch_driver_version
|
|
elif path_chromedriver:
|
|
try:
|
|
make_driver_executable_if_not(path_chromedriver)
|
|
except Exception as e:
|
|
logging.debug(
|
|
"\nWarning: Could not make chromedriver"
|
|
" executable: %s" % e
|
|
)
|
|
with suppress(Exception):
|
|
output = subprocess.check_output(
|
|
'"%s" --version' % path_chromedriver, shell=True
|
|
)
|
|
if IS_WINDOWS:
|
|
output = output.decode("latin1")
|
|
else:
|
|
output = output.decode("utf-8")
|
|
full_ch_driver_version = output.split(" ")[1]
|
|
output = full_ch_driver_version.split(".")[0]
|
|
if int(output) >= 2:
|
|
ch_driver_version = output
|
|
if driver_version == "keep":
|
|
use_version = ch_driver_version
|
|
disable_build_check = True
|
|
uc_driver_version = None
|
|
if use_uc:
|
|
if use_br_version_for_uc or driver_version == "mlatest":
|
|
uc_driver_version = get_uc_driver_version(full=True)
|
|
full_ch_driver_version = uc_driver_version
|
|
else:
|
|
uc_driver_version = get_uc_driver_version()
|
|
if multi_proxy:
|
|
sb_config.multi_proxy = True
|
|
if uc_driver_version and driver_version == "keep":
|
|
driver_version = uc_driver_version
|
|
use_version = find_chromedriver_version_to_use(
|
|
use_version, driver_version
|
|
)
|
|
if headless1:
|
|
# developer.chrome.com/blog/removing-headless-old-from-chrome
|
|
with suppress(Exception):
|
|
if int(str(use_version).split(".")[0]) >= 132:
|
|
headless1 = False
|
|
headless2 = True
|
|
if headless2:
|
|
try:
|
|
if (
|
|
use_version == "latest"
|
|
or int(str(use_version).split(".")[0]) >= 109
|
|
):
|
|
chrome_options.add_argument("--headless=new")
|
|
else:
|
|
chrome_options.add_argument("--headless=chrome")
|
|
except Exception:
|
|
chrome_options.add_argument("--headless=new")
|
|
elif headless and undetectable:
|
|
try:
|
|
int_use_version = int(str(use_version).split(".")[0])
|
|
if int_use_version >= 109:
|
|
chrome_options.add_argument("--headless=new")
|
|
elif (
|
|
int_use_version >= 96
|
|
and int_use_version <= 108
|
|
):
|
|
chrome_options.add_argument("--headless=chrome")
|
|
else:
|
|
pass # Will need Xvfb on Linux
|
|
except Exception:
|
|
pass # Will need Xvfb on Linux
|
|
elif headless:
|
|
if (
|
|
"--headless" not in chrome_options.arguments
|
|
and "--headless=old" not in chrome_options.arguments
|
|
):
|
|
if headless1:
|
|
chrome_options.add_argument("--headless=old")
|
|
else:
|
|
chrome_options.add_argument("--headless")
|
|
if LOCAL_CHROMEDRIVER and os.path.exists(LOCAL_CHROMEDRIVER):
|
|
try:
|
|
make_driver_executable_if_not(LOCAL_CHROMEDRIVER)
|
|
except Exception as e:
|
|
logging.debug(
|
|
"\nWarning: Could not make chromedriver"
|
|
" executable: %s" % e
|
|
)
|
|
make_uc_driver_from_chromedriver = False
|
|
local_ch_exists = (
|
|
LOCAL_CHROMEDRIVER and os.path.exists(LOCAL_CHROMEDRIVER)
|
|
)
|
|
"""If no LOCAL_CHROMEDRIVER, but path_chromedriver, and the
|
|
browser version nearly matches the driver version, then use
|
|
the path_chromedriver instead of downloading a new driver.
|
|
Eg. 116.0.* for both is close, but not 116.0.* and 116.1.*"""
|
|
browser_driver_close_match = False
|
|
if (
|
|
path_chromedriver
|
|
and full_ch_version
|
|
and full_ch_driver_version
|
|
):
|
|
full_ch_v_p = full_ch_version.split(".")[0:2]
|
|
full_ch_driver_v_p = full_ch_driver_version.split(".")[0:2]
|
|
if (
|
|
full_ch_v_p == full_ch_driver_v_p
|
|
or driver_version == "keep"
|
|
):
|
|
browser_driver_close_match = True
|
|
# If not ARM MAC and need to use uc_driver (and it's missing),
|
|
# and already have chromedriver with the correct version,
|
|
# then copy chromedriver to uc_driver (and it'll get patched).
|
|
if (
|
|
not IS_ARM_MAC
|
|
and use_uc
|
|
and (
|
|
(
|
|
(local_ch_exists or path_chromedriver)
|
|
and use_version == ch_driver_version
|
|
and (
|
|
not os.path.exists(LOCAL_UC_DRIVER)
|
|
or uc_driver_version != use_version
|
|
)
|
|
)
|
|
or (
|
|
local_ch_exists
|
|
and use_version == "latest"
|
|
and not os.path.exists(LOCAL_UC_DRIVER)
|
|
)
|
|
)
|
|
):
|
|
make_uc_driver_from_chromedriver = True
|
|
elif (
|
|
(use_uc and not os.path.exists(LOCAL_UC_DRIVER))
|
|
or (not use_uc and not path_chromedriver)
|
|
or (
|
|
not use_uc
|
|
and use_version != "latest" # Browser version detected
|
|
and (ch_driver_version or not local_ch_exists)
|
|
and (
|
|
use_version.split(".")[0] != ch_driver_version
|
|
or (
|
|
not local_ch_exists
|
|
and use_version.isnumeric()
|
|
and int(use_version) >= 115
|
|
and not browser_driver_close_match
|
|
)
|
|
)
|
|
)
|
|
or (
|
|
use_uc
|
|
and use_version != "latest" # Browser version detected
|
|
and uc_driver_version != use_version
|
|
)
|
|
or (
|
|
full_ch_driver_version # Also used for the uc_driver
|
|
and driver_version
|
|
and len(str(driver_version).split(".")) == 4
|
|
and full_ch_driver_version != driver_version
|
|
)
|
|
):
|
|
# chromedriver download needed in the seleniumbase/drivers dir
|
|
from seleniumbase.console_scripts import sb_install
|
|
args = " ".join(sys.argv)
|
|
if not ("-n" in sys.argv or " -n=" in args or args == "-c"):
|
|
# (Not multithreaded)
|
|
sys_args = sys.argv # Save a copy of current sys args
|
|
msg = "chromedriver update needed. Getting it now:"
|
|
if not path_chromedriver:
|
|
msg = "chromedriver not found. Getting it now:"
|
|
if use_uc and not os.path.exists(LOCAL_UC_DRIVER):
|
|
msg = "uc_driver not found. Getting it now:"
|
|
if use_uc and os.path.exists(LOCAL_UC_DRIVER):
|
|
msg = "uc_driver update needed. Getting it now:"
|
|
log_d("\nWarning: %s" % msg)
|
|
force_uc = False
|
|
intel_for_uc = False
|
|
if use_uc:
|
|
force_uc = True
|
|
if IS_ARM_MAC and use_uc:
|
|
intel_for_uc = True # Use Intel's driver for UC Mode
|
|
try:
|
|
sb_install.main(
|
|
override="chromedriver %s" % use_version,
|
|
intel_for_uc=intel_for_uc,
|
|
force_uc=force_uc,
|
|
)
|
|
except Exception:
|
|
d_latest = get_latest_chromedriver_version()
|
|
if (
|
|
d_latest
|
|
and use_version != "latest"
|
|
and int(use_version) > int(d_latest.split(".")[0])
|
|
):
|
|
disable_build_check = True
|
|
d_latest_major = d_latest.split(".")[0]
|
|
if (
|
|
not path_chromedriver
|
|
or (
|
|
ch_driver_version
|
|
and (
|
|
int(ch_driver_version)
|
|
< int(d_latest_major)
|
|
)
|
|
)
|
|
):
|
|
sb_install.main(override="chromedriver latest")
|
|
sys.argv = sys_args # Put back the original sys args
|
|
else:
|
|
# (Multithreaded)
|
|
chromedriver_fixing_lock = fasteners.InterProcessLock(
|
|
constants.MultiBrowser.DRIVER_FIXING_LOCK
|
|
)
|
|
with chromedriver_fixing_lock:
|
|
with suppress(Exception):
|
|
shared_utils.make_writable(
|
|
constants.MultiBrowser.DRIVER_FIXING_LOCK
|
|
)
|
|
msg = "chromedriver update needed. Getting it now:"
|
|
if not path_chromedriver:
|
|
msg = "chromedriver not found. Getting it now:"
|
|
if use_uc and not os.path.exists(LOCAL_UC_DRIVER):
|
|
msg = "uc_driver not found. Getting it now:"
|
|
if use_uc and os.path.exists(LOCAL_UC_DRIVER):
|
|
msg = "uc_driver update needed. Getting it now:"
|
|
force_uc = False
|
|
intel_for_uc = False
|
|
if use_uc:
|
|
force_uc = True
|
|
if IS_ARM_MAC and use_uc:
|
|
intel_for_uc = True # Use Intel driver for UC Mode
|
|
if os.path.exists(LOCAL_CHROMEDRIVER):
|
|
with suppress(Exception):
|
|
output = subprocess.check_output(
|
|
'"%s" --version' % LOCAL_CHROMEDRIVER,
|
|
shell=True,
|
|
)
|
|
if IS_WINDOWS:
|
|
output = output.decode("latin1")
|
|
else:
|
|
output = output.decode("utf-8")
|
|
full_ch_driver_version = output.split(" ")[1]
|
|
output = full_ch_driver_version.split(".")[0]
|
|
if int(output) >= 2:
|
|
ch_driver_version = output
|
|
if (
|
|
(
|
|
not use_uc
|
|
and not os.path.exists(LOCAL_CHROMEDRIVER)
|
|
)
|
|
or (use_uc and not os.path.exists(LOCAL_UC_DRIVER))
|
|
or (
|
|
not use_uc
|
|
and (
|
|
use_version.split(".")[0]
|
|
!= ch_driver_version
|
|
)
|
|
)
|
|
or (
|
|
use_uc
|
|
and (
|
|
use_version.split(".")[0]
|
|
!= get_uc_driver_version()
|
|
)
|
|
)
|
|
):
|
|
log_d("\nWarning: %s" % msg)
|
|
sys_args = sys.argv # Save a copy of sys args
|
|
try:
|
|
sb_install.main(
|
|
override="chromedriver %s" % use_version,
|
|
intel_for_uc=intel_for_uc,
|
|
force_uc=force_uc,
|
|
)
|
|
except Exception:
|
|
d_latest = get_latest_chromedriver_version()
|
|
if (
|
|
d_latest
|
|
and use_version != "latest"
|
|
and (
|
|
int(use_version)
|
|
> int(d_latest.split(".")[0])
|
|
)
|
|
):
|
|
disable_build_check = True
|
|
d_latest_major = d_latest.split(".")[0]
|
|
if (
|
|
not path_chromedriver
|
|
or (
|
|
ch_driver_version
|
|
and (
|
|
int(ch_driver_version)
|
|
< int(d_latest_major)
|
|
)
|
|
)
|
|
):
|
|
sb_install.main(
|
|
override="chromedriver latest"
|
|
)
|
|
finally:
|
|
sys.argv = sys_args # Put back original args
|
|
service_args = []
|
|
if disable_build_check:
|
|
service_args = ["--disable-build-check"]
|
|
if use_uc:
|
|
uc_lock = fasteners.InterProcessLock(
|
|
constants.MultiBrowser.DRIVER_FIXING_LOCK
|
|
)
|
|
with uc_lock: # Avoid multithreaded issues
|
|
with suppress(Exception):
|
|
shared_utils.make_writable(
|
|
constants.MultiBrowser.DRIVER_FIXING_LOCK
|
|
)
|
|
if make_uc_driver_from_chromedriver:
|
|
if os.path.exists(LOCAL_CHROMEDRIVER):
|
|
with suppress(Exception):
|
|
make_driver_executable_if_not(
|
|
LOCAL_CHROMEDRIVER
|
|
)
|
|
shutil.copy2(LOCAL_CHROMEDRIVER, LOCAL_UC_DRIVER)
|
|
elif os.path.exists(path_chromedriver):
|
|
with suppress(Exception):
|
|
make_driver_executable_if_not(
|
|
path_chromedriver
|
|
)
|
|
shutil.copy2(path_chromedriver, LOCAL_UC_DRIVER)
|
|
try:
|
|
make_driver_executable_if_not(LOCAL_UC_DRIVER)
|
|
except Exception as e:
|
|
logging.debug(
|
|
"\nWarning: Could not make uc_driver"
|
|
" executable: %s" % e
|
|
)
|
|
if not headless or not IS_LINUX or use_uc:
|
|
uc_activated = False
|
|
try:
|
|
if os.path.exists(LOCAL_CHROMEDRIVER) or use_uc:
|
|
if headless and not IS_LINUX:
|
|
undetectable = False # No support for headless
|
|
use_uc = is_using_uc(undetectable, browser_name)
|
|
if use_uc:
|
|
from seleniumbase import undetected
|
|
from urllib.error import URLError
|
|
if IS_LINUX:
|
|
if "--headless" in (
|
|
chrome_options.arguments
|
|
):
|
|
chrome_options.arguments.remove(
|
|
"--headless"
|
|
)
|
|
if "--headless=old" in (
|
|
chrome_options.arguments
|
|
):
|
|
chrome_options.arguments.remove(
|
|
"--headless=old"
|
|
)
|
|
uc_chrome_version = None
|
|
if (
|
|
use_version.isnumeric()
|
|
and int(use_version) >= 72
|
|
):
|
|
uc_chrome_version = int(use_version)
|
|
elif (
|
|
str(use_version).split(".")[0].isnumeric()
|
|
and int(str(use_version).split(".")[0]) >= 72
|
|
):
|
|
uc_chrome_version = (
|
|
int(str(use_version).split(".")[0])
|
|
)
|
|
cdp_events = uc_cdp_events
|
|
cert = "unable to get local issuer certificate"
|
|
mac_certificate_error = False
|
|
if (
|
|
use_version.isnumeric()
|
|
and int(use_version) <= 74
|
|
):
|
|
chrome_options.add_experimental_option(
|
|
"w3c", True
|
|
)
|
|
if (
|
|
(not user_agent or "Headless" in user_agent)
|
|
and uc_chrome_version
|
|
and uc_chrome_version >= 117
|
|
and (headless or headless2)
|
|
and hasattr(sb_config, "uc_agent_cache")
|
|
):
|
|
user_agent = sb_config.uc_agent_cache
|
|
chrome_options.add_argument(
|
|
"--user-agent=%s" % user_agent
|
|
)
|
|
with suppress(Exception):
|
|
if (
|
|
(
|
|
not user_agent
|
|
or "Headless" in user_agent
|
|
)
|
|
and uc_chrome_version
|
|
and uc_chrome_version >= 117
|
|
and (headless or headless2)
|
|
and chromium_arg != "decoy"
|
|
):
|
|
from seleniumbase.console_scripts import (
|
|
sb_install
|
|
)
|
|
sb_config.uc_user_agent_cache = True
|
|
headless_options = _set_chrome_options(
|
|
browser_name,
|
|
downloads_path,
|
|
True, # headless
|
|
locale_code,
|
|
None, # proxy_string
|
|
None, # proxy_auth
|
|
None, # proxy_user
|
|
None, # proxy_pass
|
|
None, # proxy_bypass_list
|
|
None, # proxy_pac_url
|
|
None, # multi_proxy
|
|
None, # user_agent
|
|
None, # recorder_ext
|
|
disable_cookies,
|
|
disable_js,
|
|
disable_csp,
|
|
enable_ws,
|
|
enable_sync,
|
|
use_auto_ext,
|
|
False, # undetectable
|
|
False, # uc_cdp_events
|
|
False, # uc_subprocess
|
|
False, # log_cdp_events
|
|
no_sandbox,
|
|
disable_gpu,
|
|
False, # headless1
|
|
False, # headless2
|
|
incognito,
|
|
guest_mode,
|
|
dark_mode,
|
|
None, # devtools
|
|
remote_debug,
|
|
enable_3d_apis,
|
|
swiftshader,
|
|
None, # ad_block_on
|
|
None, # host_resolver_rules
|
|
block_images,
|
|
do_not_track,
|
|
None, # chromium_arg
|
|
None, # user_data_dir
|
|
None, # extension_zip
|
|
None, # extension_dir
|
|
None, # disable_features
|
|
binary_location,
|
|
driver_version,
|
|
page_load_strategy,
|
|
use_wire,
|
|
external_pdf,
|
|
servername,
|
|
mobile_emulator,
|
|
device_width,
|
|
device_height,
|
|
device_pixel_ratio,
|
|
)
|
|
if (
|
|
not path_chromedriver
|
|
or (
|
|
ch_driver_version
|
|
and use_version
|
|
and (
|
|
int(ch_driver_version)
|
|
< int(str(
|
|
use_version).split(".")[0]
|
|
)
|
|
)
|
|
)
|
|
):
|
|
sb_install.main(
|
|
override="chromedriver %s"
|
|
% use_version,
|
|
intel_for_uc=False,
|
|
force_uc=False,
|
|
)
|
|
d_b_c = "--disable-build-check"
|
|
if os.path.exists(LOCAL_CHROMEDRIVER):
|
|
service = ChromeService(
|
|
executable_path=LOCAL_CHROMEDRIVER,
|
|
log_output=os.devnull,
|
|
service_args=[d_b_c],
|
|
)
|
|
driver = webdriver.Chrome(
|
|
service=service,
|
|
options=headless_options,
|
|
)
|
|
else:
|
|
service = ChromeService(
|
|
log_output=os.devnull,
|
|
service_args=[d_b_c],
|
|
)
|
|
driver = webdriver.Chrome(
|
|
service=service,
|
|
options=headless_options,
|
|
)
|
|
with suppress(Exception):
|
|
user_agent = driver.execute_script(
|
|
"return navigator.userAgent;"
|
|
)
|
|
if (
|
|
major_chrome_version
|
|
and full_ch_version
|
|
and full_ch_version.count(".") == 3
|
|
and full_ch_version in user_agent
|
|
):
|
|
mcv = major_chrome_version
|
|
user_agent = user_agent.replace(
|
|
"Chrome/%s" % full_ch_version,
|
|
"Chrome/%s.0.0.0" % mcv
|
|
)
|
|
user_agent = user_agent.replace(
|
|
"Headless", ""
|
|
)
|
|
chrome_options.add_argument(
|
|
"--user-agent=%s" % user_agent
|
|
)
|
|
sb_config.uc_agent_cache = user_agent
|
|
driver.quit()
|
|
uc_path = None
|
|
if os.path.exists(LOCAL_UC_DRIVER):
|
|
uc_path = LOCAL_UC_DRIVER
|
|
uc_path = os.path.realpath(uc_path)
|
|
try:
|
|
driver = undetected.Chrome(
|
|
options=chrome_options,
|
|
user_data_dir=user_data_dir,
|
|
driver_executable_path=uc_path,
|
|
browser_executable_path=b_path,
|
|
enable_cdp_events=cdp_events,
|
|
headless=False, # Xvfb needed!
|
|
version_main=uc_chrome_version,
|
|
use_subprocess=True, # Always!
|
|
)
|
|
uc_activated = True
|
|
except URLError as e:
|
|
if (
|
|
IS_MAC
|
|
and hasattr(e, "args")
|
|
and isinstance(e.args, (list, tuple))
|
|
and len(e.args) > 0
|
|
and cert in e.args[0]
|
|
):
|
|
mac_certificate_error = True
|
|
else:
|
|
raise
|
|
except SessionNotCreatedException:
|
|
time.sleep(0.2)
|
|
driver = undetected.Chrome(
|
|
options=chrome_options,
|
|
user_data_dir=user_data_dir,
|
|
driver_executable_path=uc_path,
|
|
browser_executable_path=b_path,
|
|
enable_cdp_events=cdp_events,
|
|
headless=False, # Xvfb needed!
|
|
version_main=uc_chrome_version,
|
|
use_subprocess=True, # Always!
|
|
)
|
|
uc_activated = True
|
|
if mac_certificate_error:
|
|
cf_lock_path = (
|
|
constants.MultiBrowser.CERT_FIXING_LOCK
|
|
)
|
|
cf_lock = fasteners.InterProcessLock(
|
|
constants.MultiBrowser.CERT_FIXING_LOCK
|
|
)
|
|
if not os.path.exists(cf_lock_path):
|
|
# Avoid multithreaded issues
|
|
with cf_lock:
|
|
with suppress(Exception):
|
|
shared_utils.make_writable(
|
|
cf_lock_path
|
|
)
|
|
# Install Python Certificates (MAC)
|
|
os.system(
|
|
r"bash /Applications/Python*/"
|
|
r"Install\ "
|
|
r"Certificates.command"
|
|
)
|
|
driver = undetected.Chrome(
|
|
options=chrome_options,
|
|
user_data_dir=user_data_dir,
|
|
driver_executable_path=uc_path,
|
|
browser_executable_path=b_path,
|
|
enable_cdp_events=cdp_events,
|
|
headless=False, # Xvfb needed!
|
|
version_main=uc_chrome_version,
|
|
use_subprocess=True, # Always!
|
|
)
|
|
uc_activated = True
|
|
else:
|
|
if (
|
|
use_version.isnumeric()
|
|
and int(use_version) <= 74
|
|
):
|
|
chrome_options.add_experimental_option(
|
|
"w3c", True
|
|
)
|
|
service = ChromeService(
|
|
executable_path=LOCAL_CHROMEDRIVER,
|
|
log_output=os.devnull,
|
|
service_args=service_args,
|
|
)
|
|
driver = webdriver.Chrome(
|
|
service=service,
|
|
options=chrome_options,
|
|
)
|
|
else:
|
|
service = ChromeService(
|
|
log_output=os.devnull,
|
|
service_args=service_args,
|
|
)
|
|
driver = webdriver.Chrome(
|
|
service=service,
|
|
options=chrome_options,
|
|
)
|
|
except Exception as e:
|
|
if not hasattr(e, "msg"):
|
|
raise
|
|
auto_upgrade_chromedriver = False
|
|
if "This version of ChromeDriver only supports" in e.msg:
|
|
auto_upgrade_chromedriver = True
|
|
elif "Chrome version must be between" in e.msg:
|
|
auto_upgrade_chromedriver = True
|
|
elif "Missing or invalid capabilities" in e.msg:
|
|
chrome_options.add_experimental_option("w3c", True)
|
|
service = ChromeService(
|
|
log_output=os.devnull,
|
|
service_args=service_args,
|
|
)
|
|
with warnings.catch_warnings():
|
|
warnings.simplefilter(
|
|
"ignore", category=DeprecationWarning
|
|
)
|
|
driver = webdriver.Chrome(
|
|
service=service, options=chrome_options
|
|
)
|
|
return extend_driver(driver, proxy_auth, use_uc)
|
|
if not auto_upgrade_chromedriver:
|
|
raise # Not an obvious fix.
|
|
else:
|
|
pass # Try upgrading ChromeDriver to match Chrome.
|
|
mcv = None # Major Chrome Version
|
|
if "Current browser version is " in e.msg:
|
|
line = e.msg.split("Current browser version is ")[1]
|
|
browser_version = line.split(" ")[0]
|
|
major_chrome_version = browser_version.split(".")[0]
|
|
if (
|
|
major_chrome_version.isnumeric()
|
|
and int(major_chrome_version) >= 86
|
|
):
|
|
mcv = major_chrome_version
|
|
mcv = find_chromedriver_version_to_use(
|
|
mcv, driver_version
|
|
)
|
|
headless_options = _set_chrome_options(
|
|
browser_name,
|
|
downloads_path,
|
|
True, # headless
|
|
locale_code,
|
|
None, # proxy_string
|
|
None, # proxy_auth
|
|
None, # proxy_user
|
|
None, # proxy_pass
|
|
None, # proxy_bypass_list
|
|
None, # proxy_pac_url
|
|
None, # multi_proxy
|
|
None, # user_agent
|
|
None, # recorder_ext
|
|
disable_cookies,
|
|
disable_js,
|
|
disable_csp,
|
|
enable_ws,
|
|
enable_sync,
|
|
use_auto_ext,
|
|
False, # undetectable
|
|
False, # uc_cdp_events
|
|
False, # uc_subprocess
|
|
False, # log_cdp_events
|
|
no_sandbox,
|
|
disable_gpu,
|
|
False, # headless1
|
|
False, # headless2
|
|
incognito,
|
|
guest_mode,
|
|
dark_mode,
|
|
None, # devtools
|
|
remote_debug,
|
|
enable_3d_apis,
|
|
swiftshader,
|
|
None, # ad_block_on
|
|
None, # host_resolver_rules
|
|
block_images,
|
|
do_not_track,
|
|
None, # chromium_arg
|
|
None, # user_data_dir
|
|
None, # extension_zip
|
|
None, # extension_dir
|
|
None, # disable_features
|
|
binary_location,
|
|
driver_version,
|
|
page_load_strategy,
|
|
use_wire,
|
|
external_pdf,
|
|
servername,
|
|
mobile_emulator,
|
|
device_width,
|
|
device_height,
|
|
device_pixel_ratio,
|
|
)
|
|
args = " ".join(sys.argv)
|
|
if "-n" in sys.argv or " -n=" in args or args == "-c":
|
|
chromedriver_fixing_lock = fasteners.InterProcessLock(
|
|
constants.MultiBrowser.DRIVER_FIXING_LOCK
|
|
)
|
|
with chromedriver_fixing_lock:
|
|
with suppress(Exception):
|
|
shared_utils.make_writable(
|
|
constants.MultiBrowser.DRIVER_FIXING_LOCK
|
|
)
|
|
if not _was_driver_repaired():
|
|
_repair_chromedriver(
|
|
chrome_options, headless_options, mcv
|
|
)
|
|
_mark_driver_repaired()
|
|
else:
|
|
if not _was_driver_repaired():
|
|
_repair_chromedriver(
|
|
chrome_options, headless_options, mcv
|
|
)
|
|
_mark_driver_repaired()
|
|
if os.path.exists(LOCAL_CHROMEDRIVER):
|
|
service = ChromeService(
|
|
executable_path=LOCAL_CHROMEDRIVER,
|
|
log_output=os.devnull,
|
|
service_args=["--disable-build-check"],
|
|
)
|
|
driver = webdriver.Chrome(
|
|
service=service,
|
|
options=chrome_options,
|
|
)
|
|
else:
|
|
service = ChromeService(
|
|
log_output=os.devnull,
|
|
service_args=["--disable-build-check"],
|
|
)
|
|
driver = webdriver.Chrome(
|
|
service=service,
|
|
options=chrome_options,
|
|
)
|
|
driver.default_get = driver.get # Save copy of original
|
|
driver.cdp = None # Set a placeholder
|
|
driver._is_using_cdp = False
|
|
driver._is_connected = True
|
|
if uc_activated:
|
|
driver.get = lambda url: uc_special_open_if_cf(
|
|
driver,
|
|
url,
|
|
proxy_string,
|
|
mobile_emulator,
|
|
device_width,
|
|
device_height,
|
|
device_pixel_ratio,
|
|
)
|
|
driver.uc_open = lambda url: uc_open(driver, url)
|
|
driver.uc_open_with_tab = (
|
|
lambda url: uc_open_with_tab(driver, url)
|
|
)
|
|
driver.uc_open_with_reconnect = (
|
|
lambda *args, **kwargs: uc_open_with_reconnect(
|
|
driver, *args, **kwargs
|
|
)
|
|
)
|
|
driver.uc_open_with_disconnect = (
|
|
lambda *args, **kwargs: uc_open_with_disconnect(
|
|
driver, *args, **kwargs
|
|
)
|
|
)
|
|
driver.uc_click = lambda *args, **kwargs: uc_click(
|
|
driver, *args, **kwargs
|
|
)
|
|
driver.uc_activate_cdp_mode = (
|
|
lambda *args, **kwargs: uc_activate_cdp_mode(
|
|
driver, *args, **kwargs
|
|
)
|
|
)
|
|
driver.uc_open_with_cdp_mode = (
|
|
lambda *args, **kwargs: uc_open_with_cdp_mode(
|
|
driver, *args, **kwargs
|
|
)
|
|
)
|
|
driver.uc_gui_press_key = (
|
|
lambda *args, **kwargs: uc_gui_press_key(
|
|
driver, *args, **kwargs
|
|
)
|
|
)
|
|
driver.uc_gui_press_keys = (
|
|
lambda *args, **kwargs: uc_gui_press_keys(
|
|
driver, *args, **kwargs
|
|
)
|
|
)
|
|
driver.uc_gui_write = (
|
|
lambda *args, **kwargs: uc_gui_write(
|
|
driver, *args, **kwargs
|
|
)
|
|
)
|
|
driver.uc_gui_click_x_y = (
|
|
lambda *args, **kwargs: uc_gui_click_x_y(
|
|
driver, *args, **kwargs
|
|
)
|
|
)
|
|
driver.uc_gui_click_captcha = (
|
|
lambda *args, **kwargs: uc_gui_click_captcha(
|
|
driver, *args, **kwargs
|
|
)
|
|
)
|
|
driver.uc_gui_click_cf = (
|
|
lambda *args, **kwargs: uc_gui_click_cf(
|
|
driver, *args, **kwargs
|
|
)
|
|
)
|
|
driver.uc_gui_click_rc = (
|
|
lambda *args, **kwargs: uc_gui_click_rc(
|
|
driver, *args, **kwargs
|
|
)
|
|
)
|
|
driver.uc_gui_handle_captcha = (
|
|
lambda *args, **kwargs: uc_gui_handle_captcha(
|
|
driver, *args, **kwargs
|
|
)
|
|
)
|
|
driver.uc_gui_handle_cf = (
|
|
lambda *args, **kwargs: uc_gui_handle_cf(
|
|
driver, *args, **kwargs
|
|
)
|
|
)
|
|
driver.uc_gui_handle_rc = (
|
|
lambda *args, **kwargs: uc_gui_handle_rc(
|
|
driver, *args, **kwargs
|
|
)
|
|
)
|
|
driver.uc_switch_to_frame = (
|
|
lambda *args, **kwargs: uc_switch_to_frame(
|
|
driver, *args, **kwargs
|
|
)
|
|
)
|
|
driver._is_hidden = (headless or headless2)
|
|
driver._is_using_uc = True
|
|
with suppress(Exception):
|
|
if int(uc_driver_version) >= 133:
|
|
for window_handle in driver.window_handles:
|
|
driver.switch_to.window(window_handle)
|
|
if driver.current_url.startswith(
|
|
"chrome-extension://"
|
|
):
|
|
driver.close()
|
|
time.sleep(0.003)
|
|
driver.switch_to.window(driver.window_handles[0])
|
|
time.sleep(0.003)
|
|
driver.connect()
|
|
time.sleep(0.003)
|
|
if mobile_emulator:
|
|
uc_metrics = {}
|
|
if (
|
|
isinstance(device_width, int)
|
|
and isinstance(device_height, int)
|
|
and isinstance(device_pixel_ratio, (int, float))
|
|
):
|
|
uc_metrics["width"] = device_width
|
|
uc_metrics["height"] = device_height
|
|
uc_metrics["pixelRatio"] = device_pixel_ratio
|
|
else:
|
|
uc_metrics["width"] = constants.Mobile.WIDTH
|
|
uc_metrics["height"] = constants.Mobile.HEIGHT
|
|
uc_metrics["pixelRatio"] = constants.Mobile.RATIO
|
|
set_device_metrics_override = dict(
|
|
{
|
|
"width": uc_metrics["width"],
|
|
"height": uc_metrics["height"],
|
|
"deviceScaleFactor": uc_metrics["pixelRatio"],
|
|
"mobile": True
|
|
}
|
|
)
|
|
with suppress(Exception):
|
|
driver.execute_cdp_cmd(
|
|
'Emulation.setDeviceMetricsOverride',
|
|
set_device_metrics_override
|
|
)
|
|
return extend_driver(driver, proxy_auth, use_uc)
|
|
else: # Running headless on Linux (and not using --uc)
|
|
try:
|
|
driver = webdriver.Chrome(options=chrome_options)
|
|
return extend_driver(driver, proxy_auth, use_uc)
|
|
except Exception as e:
|
|
if not hasattr(e, "msg"):
|
|
raise
|
|
auto_upgrade_chromedriver = False
|
|
if "This version of ChromeDriver only supports" in e.msg:
|
|
auto_upgrade_chromedriver = True
|
|
elif "Chrome version must be between" in e.msg:
|
|
auto_upgrade_chromedriver = True
|
|
elif "Missing or invalid capabilities" in e.msg:
|
|
chrome_options.add_experimental_option("w3c", True)
|
|
service = ChromeService(
|
|
log_output=os.devnull,
|
|
service_args=["--disable-build-check"],
|
|
)
|
|
with warnings.catch_warnings():
|
|
warnings.simplefilter(
|
|
"ignore", category=DeprecationWarning
|
|
)
|
|
driver = webdriver.Chrome(
|
|
service=service, options=chrome_options
|
|
)
|
|
return extend_driver(driver, proxy_auth, use_uc)
|
|
mcv = None # Major Chrome Version
|
|
if "Current browser version is " in e.msg:
|
|
line = e.msg.split("Current browser version is ")[1]
|
|
browser_version = line.split(" ")[0]
|
|
major_chrome_version = browser_version.split(".")[0]
|
|
if (
|
|
major_chrome_version.isnumeric()
|
|
and int(major_chrome_version) >= 86
|
|
):
|
|
mcv = major_chrome_version
|
|
if auto_upgrade_chromedriver:
|
|
args = " ".join(sys.argv)
|
|
if "-n" in sys.argv or " -n=" in args or args == "-c":
|
|
chromedr_fixing_lock = fasteners.InterProcessLock(
|
|
constants.MultiBrowser.DRIVER_FIXING_LOCK
|
|
)
|
|
D_F_L = constants.MultiBrowser.DRIVER_FIXING_LOCK
|
|
with chromedr_fixing_lock:
|
|
with suppress(Exception):
|
|
shared_utils.make_writable(D_F_L)
|
|
if not _was_driver_repaired():
|
|
with suppress(Exception):
|
|
_repair_chromedriver(
|
|
chrome_options, chrome_options, mcv
|
|
)
|
|
_mark_driver_repaired()
|
|
else:
|
|
if not _was_driver_repaired():
|
|
with suppress(Exception):
|
|
_repair_chromedriver(
|
|
chrome_options, chrome_options, mcv
|
|
)
|
|
_mark_driver_repaired()
|
|
with suppress(Exception):
|
|
service = ChromeService(
|
|
log_output=os.devnull,
|
|
service_args=["--disable-build-check"],
|
|
)
|
|
driver = webdriver.Chrome(
|
|
service=service,
|
|
options=chrome_options,
|
|
)
|
|
return extend_driver(driver, proxy_auth, use_uc)
|
|
# Use the virtual display on Linux during headless errors
|
|
logging.debug(
|
|
"\nWarning: Chrome failed to launch in"
|
|
" headless mode. Attempting to use the"
|
|
" SeleniumBase virtual display on Linux..."
|
|
)
|
|
if "--headless" in chrome_options.arguments:
|
|
chrome_options.arguments.remove("--headless")
|
|
if "--headless=old" in chrome_options.arguments:
|
|
chrome_options.arguments.remove("--headless=old")
|
|
service = ChromeService(
|
|
log_output=os.devnull,
|
|
service_args=["--disable-build-check"]
|
|
)
|
|
driver = webdriver.Chrome(
|
|
service=service, options=chrome_options
|
|
)
|
|
return extend_driver(driver, proxy_auth, use_uc)
|
|
except Exception as original_exception:
|
|
if use_uc:
|
|
raise
|
|
# Try again if Chrome didn't launch
|
|
with suppress(Exception):
|
|
service = ChromeService(service_args=["--disable-build-check"])
|
|
driver = webdriver.Chrome(
|
|
service=service, options=chrome_options
|
|
)
|
|
return extend_driver(driver, proxy_auth, use_uc)
|
|
if user_data_dir:
|
|
print("\nUnable to set user_data_dir while starting Chrome!\n")
|
|
raise
|
|
elif mobile_emulator:
|
|
print("\nFailed to start Chrome's mobile device emulator!\n")
|
|
raise
|
|
elif extension_zip or extension_dir:
|
|
print("\nUnable to load extension while starting Chrome!\n")
|
|
raise
|
|
elif headless or headless2 or IS_LINUX or proxy_string or use_wire:
|
|
raise
|
|
# Try running without any options (bare bones Chrome launch)
|
|
if LOCAL_CHROMEDRIVER and os.path.exists(LOCAL_CHROMEDRIVER):
|
|
try:
|
|
make_driver_executable_if_not(LOCAL_CHROMEDRIVER)
|
|
except Exception as e:
|
|
logging.debug(
|
|
"\nWarning: Could not make chromedriver"
|
|
" executable: %s" % e
|
|
)
|
|
service = ChromeService(
|
|
log_output=os.devnull,
|
|
service_args=["--disable-build-check"]
|
|
)
|
|
try:
|
|
driver = webdriver.Chrome(service=service)
|
|
return extend_driver(driver, proxy_auth, use_uc)
|
|
except Exception:
|
|
raise original_exception
|
|
else:
|
|
raise Exception(
|
|
"%s is not a valid browser option for this system!" % browser_name
|
|
)
|