We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
RPA.Browser.Selenium fails in cloud workers if download=TRUE
2023-05-08 12:47:36:  ==============================================================================
2023-05-08 12:47:36:  Tasks                                                                         
2023-05-08 12:47:36:  ==============================================================================
2023-05-08 12:47:46:  Minimal task                                                          | FAIL |
2023-05-08 12:47:46:  BrowserNotFoundError: Failed to start a browser:
2023-05-08 12:47:46:  - Chrome: Message: session not created: This version of ChromeDriver only supports Chrome version 113
2023-05-08 12:47:46:  Current browser version is 105.0.5195.102 with binary path /usr/bin/chromium-browser
2023-05-08 12:47:46:  Stacktrace:
2023-05-08 12:47:46:  #0 0x55e4a398c133 <unknown>
2023-05-08 12:47:46:  #1 0x55e4a36c0966 <unknown>
2023-05-08 12:47:46:  #2 0x55e4a36ee5ec <unknown>
2023-05-08 12:47:46:  #3 0x55e4a36e98da <unknown>
2023-05-08 12:47:46:  #4 0x55e4a36e607b <unknown>
2023-05-08 12:47:46:  #5 0x55e4a37259ad <unknown>
2023-05-08 12:47:46:  #6 0x55e4a372518f <unknown>
2023-05-08 12:47:46:  #7 0x55e4a371c9a3 <unknown>
2023-05-08 12:47:46:  #8 0x55e4a36f146a <unknown>
2023-05-08 12:47:46:  #9 0x55e4a36f255e <unknown>
2023-05-08 12:47:46:  #10 0x55e4a394bcae <unknown>
2023-05-08 12:47:46:  #11 0x55e4a394f8fe <unknown>
2023-05-08 12:47:46:  #12 0x55e4a3958f20 <unknown>
2023-05-08 12:47:46:  #13 0x55e4a3950923 <unknown>
2023-05-08 12:47:46:      [ Message content over the limit has been removed. ]
2023-05-08 12:47:46:  #0 0x55c657673273 <unknown>
2023-05-08 12:47:46:  #1 0x55c65738e7a1 <unknown>
2023-05-08 12:47:46:  #2 0x55c6573babeb <unknown>
2023-05-08 12:47:46:  #3 0x55c6573b5fdc <unknown>
2023-05-08 12:47:46:  #4 0x55c6573f7695 <unknown>
2023-05-08 12:47:46:  #5 0x55c6573ee723 <unknown>
2023-05-08 12:47:46:  #6 0x55c6573c17d1 <unknown>
2023-05-08 12:47:46:  #7 0x55c6573c2a0e <unknown>
2023-05-08 12:47:46:  #8 0x55c657641390 <unknown>
2023-05-08 12:47:46:  #9 0x55c657643a9e <unknown>
2023-05-08 12:47:46:  #10 0x55c6576434b9 <unknown>
2023-05-08 12:47:46:  #11 0x55c6576441a5 <unknown>
2023-05-08 12:47:46:  #12 0x55c65764ae0b <unknown>
2023-05-08 12:47:46:  #13 0x55c65764456e <unknown>
2023-05-08 12:47:46:  #14 0x55c657625373 <unknown>
2023-05-08 12:47:46:  #15 0x55c65765fc58 <unknown>
2023-05-08 12:47:46:  #16 0x55c65765fd94 <unknown>
2023-05-08 12:47:46:  #17 0x55c65766d5c6 <unknown>
2023-05-08 12:47:46:  #18 0x7f8de95b36db start_thread
2023-05-08 12:47:46:  ------------------------------------------------------------------------------
2023-05-08 12:47:46:  Tasks                                                                 | FAIL |
2023-05-08 12:47:46:  1 task, 0 passed, 1 failed
2023-05-08 12:47:46:  ==============================================================================

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `packages/core/src/RPA/core/webdriver.py`
Content:
```
1 import contextlib
2 import logging
3 import os
4 import platform
5 import stat
6 from pathlib import Path
7 from typing import List, Optional
8 
9 import requests
10 from requests import Response
11 from selenium import webdriver
12 from selenium.webdriver.common.service import Service
13 from selenium.webdriver.remote.webdriver import WebDriver
14 from webdriver_manager.chrome import ChromeDriverManager
15 from webdriver_manager.core.download_manager import WDMDownloadManager
16 from webdriver_manager.core.http import WDMHttpClient
17 from webdriver_manager.core.manager import DriverManager
18 from webdriver_manager.core.utils import os_name as get_os_name
19 from webdriver_manager.firefox import GeckoDriverManager
20 from webdriver_manager.microsoft import EdgeChromiumDriverManager, IEDriverManager
21 from webdriver_manager.opera import OperaDriverManager
22 
23 from RPA.core.robocorp import robocorp_home
24 
25 
26 LOGGER = logging.getLogger(__name__)
27 
28 DRIVER_ROOT = robocorp_home() / "webdrivers"
29 AVAILABLE_DRIVERS = {
30     # Driver names taken from `webdrivermanager` and adapted to `webdriver_manager`.
31     "chrome": ChromeDriverManager,
32     "firefox": GeckoDriverManager,
33     "gecko": GeckoDriverManager,
34     "mozilla": GeckoDriverManager,
35     # NOTE: Selenium 4 dropped support for Opera.
36     #  (https://github.com/SeleniumHQ/selenium/issues/10835)
37     "opera": OperaDriverManager,
38     # NOTE: In Selenium 4 `Edge` is the same with `ChromiumEdge`.
39     "edge": EdgeChromiumDriverManager,
40     "chromiumedge": EdgeChromiumDriverManager,
41     # NOTE: IE is discontinued and not supported/encouraged anymore.
42     "ie": IEDriverManager,
43 }
44 # Available `WebDriver` classes in Selenium.
45 SUPPORTED_BROWSERS = dict(
46     {name: name.capitalize() for name in AVAILABLE_DRIVERS},
47     **{"chromiumedge": "ChromiumEdge"},
48 )
49 _DRIVER_PREFERENCE = {
50     "Windows": ["Chrome", "Firefox", "Edge"],
51     "Linux": ["Chrome", "Firefox", "Edge"],
52     "Darwin": ["Chrome", "Firefox", "Edge", "Safari"],
53     "default": ["Chrome", "Firefox"],
54 }
55 
56 
57 def _get_browser_order_from_env() -> Optional[List[str]]:
58     browsers: str = os.getenv("RPA_SELENIUM_BROWSER_ORDER", "")
59     if browsers:
60         return [browser.strip() for browser in browsers.split(sep=",")]
61 
62     return None  # meaning there's no env var to control the order
63 
64 
65 def get_browser_order() -> List[str]:
66     """Get a list of preferred browsers based on the environment variable
67     `RPA_SELENIUM_BROWSER_ORDER` if set.
68 
69     The OS dictates the order if no such env var is set.
70     """
71     browsers: Optional[List[str]] = _get_browser_order_from_env()
72     if browsers:
73         return browsers
74 
75     return _DRIVER_PREFERENCE.get(platform.system(), _DRIVER_PREFERENCE["default"])
76 
77 
78 class Downloader(WDMHttpClient):
79 
80     """Custom downloader which disables download progress reporting."""
81 
82     def get(self, url, **kwargs) -> Response:
83         resp = requests.get(url=url, verify=self._ssl_verify, stream=True, **kwargs)
84         self.validate_response(resp)
85         return resp
86 
87 
88 @contextlib.contextmanager
89 def suppress_logging():
90     """Suppress webdriver-manager logging."""
91     wdm_log = "WDM_LOG"
92     original_value = os.getenv(wdm_log, "")
93     try:
94         os.environ[wdm_log] = str(logging.NOTSET)
95         yield
96     finally:
97         os.environ[wdm_log] = original_value
98 
99 
100 def start(browser: str, service: Optional[Service] = None, **options) -> WebDriver:
101     """Start a webdriver with the given options."""
102     browser = browser.strip()
103     webdriver_factory = getattr(webdriver, browser, None)
104     if not webdriver_factory:
105         raise ValueError(f"Unsupported Selenium browser: {browser}")
106 
107     # NOTE: It is recommended to pass a `service` rather than deprecated `options`.
108     driver = webdriver_factory(service=service, **options)
109     return driver
110 
111 
112 def _to_manager(browser: str, *, root: Path) -> DriverManager:
113     browser = browser.strip()
114     manager_factory = AVAILABLE_DRIVERS.get(browser.lower())
115     if not manager_factory:
116         raise ValueError(
117             f"Unsupported browser {browser!r} for webdriver download!"
118             f" (choose from: {', '.join(SUPPORTED_BROWSERS.values())})"
119         )
120 
121     downloader = Downloader()
122     download_manager = WDMDownloadManager(downloader)
123     manager = manager_factory(path=str(root), download_manager=download_manager)
124     return manager
125 
126 
127 def _set_executable(path: str) -> None:
128     st = os.stat(path)
129     os.chmod(
130         path,
131         st.st_mode | stat.S_IXOTH | stat.S_IXGRP | stat.S_IEXEC,
132     )
133 
134 
135 def download(browser: str, root: Path = DRIVER_ROOT) -> Optional[str]:
136     """Download a webdriver binary for the given browser and return the path to it."""
137     manager = _to_manager(browser, root=root)
138     driver = manager.driver
139     resolved_os = getattr(driver, "os_type", driver.get_os_type())
140     os_name = get_os_name()
141     if os_name.lower() not in resolved_os.lower():
142         LOGGER.warning(
143             "Attempting to download incompatible driver for OS %r on OS %r! Skip",
144             resolved_os,
145             os_name,
146         )
147         return None  # incompatible driver download attempt
148 
149     with suppress_logging():
150         path: str = manager.install()
151     if platform.system() != "Windows":
152         _set_executable(path)
153     LOGGER.info("Downloaded webdriver to: %s", path)
154     return path
155
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/packages/core/src/RPA/core/webdriver.py b/packages/core/src/RPA/core/webdriver.py
--- a/packages/core/src/RPA/core/webdriver.py
+++ b/packages/core/src/RPA/core/webdriver.py
@@ -1,4 +1,5 @@
 import contextlib
+import functools
 import logging
 import os
 import platform
@@ -15,6 +16,7 @@
 from webdriver_manager.core.download_manager import WDMDownloadManager
 from webdriver_manager.core.http import WDMHttpClient
 from webdriver_manager.core.manager import DriverManager
+from webdriver_manager.core.utils import ChromeType, get_browser_version_from_os
 from webdriver_manager.core.utils import os_name as get_os_name
 from webdriver_manager.firefox import GeckoDriverManager
 from webdriver_manager.microsoft import EdgeChromiumDriverManager, IEDriverManager
@@ -109,6 +111,14 @@
     return driver
 
 
+def _is_chromium() -> bool:
+    """Detects if Chromium is used instead of Chrome no matter the platform."""
+    is_browser = lambda browser_type: bool(  # noqa: E731
+        get_browser_version_from_os(browser_type)
+    )
+    return not is_browser(ChromeType.GOOGLE) and is_browser(ChromeType.CHROMIUM)
+
+
 def _to_manager(browser: str, *, root: Path) -> DriverManager:
     browser = browser.strip()
     manager_factory = AVAILABLE_DRIVERS.get(browser.lower())
@@ -118,6 +128,10 @@
             f" (choose from: {', '.join(SUPPORTED_BROWSERS.values())})"
         )
 
+    if manager_factory == ChromeDriverManager and _is_chromium():
+        manager_factory = functools.partial(
+            manager_factory, chrome_type=ChromeType.CHROMIUM
+        )
     downloader = Downloader()
     download_manager = WDMDownloadManager(downloader)
     manager = manager_factory(path=str(root), download_manager=download_manager)

{"golden_diff": "diff --git a/packages/core/src/RPA/core/webdriver.py b/packages/core/src/RPA/core/webdriver.py\n--- a/packages/core/src/RPA/core/webdriver.py\n+++ b/packages/core/src/RPA/core/webdriver.py\n@@ -1,4 +1,5 @@\n import contextlib\n+import functools\n import logging\n import os\n import platform\n@@ -15,6 +16,7 @@\n from webdriver_manager.core.download_manager import WDMDownloadManager\n from webdriver_manager.core.http import WDMHttpClient\n from webdriver_manager.core.manager import DriverManager\n+from webdriver_manager.core.utils import ChromeType, get_browser_version_from_os\n from webdriver_manager.core.utils import os_name as get_os_name\n from webdriver_manager.firefox import GeckoDriverManager\n from webdriver_manager.microsoft import EdgeChromiumDriverManager, IEDriverManager\n@@ -109,6 +111,14 @@\n     return driver\n \n \n+def _is_chromium() -> bool:\n+    \"\"\"Detects if Chromium is used instead of Chrome no matter the platform.\"\"\"\n+    is_browser = lambda browser_type: bool(  # noqa: E731\n+        get_browser_version_from_os(browser_type)\n+    )\n+    return not is_browser(ChromeType.GOOGLE) and is_browser(ChromeType.CHROMIUM)\n+\n+\n def _to_manager(browser: str, *, root: Path) -> DriverManager:\n     browser = browser.strip()\n     manager_factory = AVAILABLE_DRIVERS.get(browser.lower())\n@@ -118,6 +128,10 @@\n             f\" (choose from: {', '.join(SUPPORTED_BROWSERS.values())})\"\n         )\n \n+    if manager_factory == ChromeDriverManager and _is_chromium():\n+        manager_factory = functools.partial(\n+            manager_factory, chrome_type=ChromeType.CHROMIUM\n+        )\n     downloader = Downloader()\n     download_manager = WDMDownloadManager(downloader)\n     manager = manager_factory(path=str(root), download_manager=download_manager)\n", "issue": "RPA.Browser.Selenium fails in cloud workers if download=TRUE\n2023-05-08 12:47:36:  ==============================================================================\r\n2023-05-08 12:47:36:  Tasks                                                                         \r\n2023-05-08 12:47:36:  ==============================================================================\r\n2023-05-08 12:47:46:  Minimal task                                                          | FAIL |\r\n2023-05-08 12:47:46:  BrowserNotFoundError: Failed to start a browser:\r\n2023-05-08 12:47:46:  - Chrome: Message: session not created: This version of ChromeDriver only supports Chrome version 113\r\n2023-05-08 12:47:46:  Current browser version is 105.0.5195.102 with binary path /usr/bin/chromium-browser\r\n2023-05-08 12:47:46:  Stacktrace:\r\n2023-05-08 12:47:46:  #0 0x55e4a398c133 <unknown>\r\n2023-05-08 12:47:46:  #1 0x55e4a36c0966 <unknown>\r\n2023-05-08 12:47:46:  #2 0x55e4a36ee5ec <unknown>\r\n2023-05-08 12:47:46:  #3 0x55e4a36e98da <unknown>\r\n2023-05-08 12:47:46:  #4 0x55e4a36e607b <unknown>\r\n2023-05-08 12:47:46:  #5 0x55e4a37259ad <unknown>\r\n2023-05-08 12:47:46:  #6 0x55e4a372518f <unknown>\r\n2023-05-08 12:47:46:  #7 0x55e4a371c9a3 <unknown>\r\n2023-05-08 12:47:46:  #8 0x55e4a36f146a <unknown>\r\n2023-05-08 12:47:46:  #9 0x55e4a36f255e <unknown>\r\n2023-05-08 12:47:46:  #10 0x55e4a394bcae <unknown>\r\n2023-05-08 12:47:46:  #11 0x55e4a394f8fe <unknown>\r\n2023-05-08 12:47:46:  #12 0x55e4a3958f20 <unknown>\r\n2023-05-08 12:47:46:  #13 0x55e4a3950923 <unknown>\r\n2023-05-08 12:47:46:      [ Message content over the limit has been removed. ]\r\n2023-05-08 12:47:46:  #0 0x55c657673273 <unknown>\r\n2023-05-08 12:47:46:  #1 0x55c65738e7a1 <unknown>\r\n2023-05-08 12:47:46:  #2 0x55c6573babeb <unknown>\r\n2023-05-08 12:47:46:  #3 0x55c6573b5fdc <unknown>\r\n2023-05-08 12:47:46:  #4 0x55c6573f7695 <unknown>\r\n2023-05-08 12:47:46:  #5 0x55c6573ee723 <unknown>\r\n2023-05-08 12:47:46:  #6 0x55c6573c17d1 <unknown>\r\n2023-05-08 12:47:46:  #7 0x55c6573c2a0e <unknown>\r\n2023-05-08 12:47:46:  #8 0x55c657641390 <unknown>\r\n2023-05-08 12:47:46:  #9 0x55c657643a9e <unknown>\r\n2023-05-08 12:47:46:  #10 0x55c6576434b9 <unknown>\r\n2023-05-08 12:47:46:  #11 0x55c6576441a5 <unknown>\r\n2023-05-08 12:47:46:  #12 0x55c65764ae0b <unknown>\r\n2023-05-08 12:47:46:  #13 0x55c65764456e <unknown>\r\n2023-05-08 12:47:46:  #14 0x55c657625373 <unknown>\r\n2023-05-08 12:47:46:  #15 0x55c65765fc58 <unknown>\r\n2023-05-08 12:47:46:  #16 0x55c65765fd94 <unknown>\r\n2023-05-08 12:47:46:  #17 0x55c65766d5c6 <unknown>\r\n2023-05-08 12:47:46:  #18 0x7f8de95b36db start_thread\r\n2023-05-08 12:47:46:  ------------------------------------------------------------------------------\r\n2023-05-08 12:47:46:  Tasks                                                                 | FAIL |\r\n2023-05-08 12:47:46:  1 task, 0 passed, 1 failed\r\n2023-05-08 12:47:46:  ==============================================================================\n", "before_files": [{"content": "import contextlib\nimport logging\nimport os\nimport platform\nimport stat\nfrom pathlib import Path\nfrom typing import List, Optional\n\nimport requests\nfrom requests import Response\nfrom selenium import webdriver\nfrom selenium.webdriver.common.service import Service\nfrom selenium.webdriver.remote.webdriver import WebDriver\nfrom webdriver_manager.chrome import ChromeDriverManager\nfrom webdriver_manager.core.download_manager import WDMDownloadManager\nfrom webdriver_manager.core.http import WDMHttpClient\nfrom webdriver_manager.core.manager import DriverManager\nfrom webdriver_manager.core.utils import os_name as get_os_name\nfrom webdriver_manager.firefox import GeckoDriverManager\nfrom webdriver_manager.microsoft import EdgeChromiumDriverManager, IEDriverManager\nfrom webdriver_manager.opera import OperaDriverManager\n\nfrom RPA.core.robocorp import robocorp_home\n\n\nLOGGER = logging.getLogger(__name__)\n\nDRIVER_ROOT = robocorp_home() / \"webdrivers\"\nAVAILABLE_DRIVERS = {\n    # Driver names taken from `webdrivermanager` and adapted to `webdriver_manager`.\n    \"chrome\": ChromeDriverManager,\n    \"firefox\": GeckoDriverManager,\n    \"gecko\": GeckoDriverManager,\n    \"mozilla\": GeckoDriverManager,\n    # NOTE: Selenium 4 dropped support for Opera.\n    #  (https://github.com/SeleniumHQ/selenium/issues/10835)\n    \"opera\": OperaDriverManager,\n    # NOTE: In Selenium 4 `Edge` is the same with `ChromiumEdge`.\n    \"edge\": EdgeChromiumDriverManager,\n    \"chromiumedge\": EdgeChromiumDriverManager,\n    # NOTE: IE is discontinued and not supported/encouraged anymore.\n    \"ie\": IEDriverManager,\n}\n# Available `WebDriver` classes in Selenium.\nSUPPORTED_BROWSERS = dict(\n    {name: name.capitalize() for name in AVAILABLE_DRIVERS},\n    **{\"chromiumedge\": \"ChromiumEdge\"},\n)\n_DRIVER_PREFERENCE = {\n    \"Windows\": [\"Chrome\", \"Firefox\", \"Edge\"],\n    \"Linux\": [\"Chrome\", \"Firefox\", \"Edge\"],\n    \"Darwin\": [\"Chrome\", \"Firefox\", \"Edge\", \"Safari\"],\n    \"default\": [\"Chrome\", \"Firefox\"],\n}\n\n\ndef _get_browser_order_from_env() -> Optional[List[str]]:\n    browsers: str = os.getenv(\"RPA_SELENIUM_BROWSER_ORDER\", \"\")\n    if browsers:\n        return [browser.strip() for browser in browsers.split(sep=\",\")]\n\n    return None  # meaning there's no env var to control the order\n\n\ndef get_browser_order() -> List[str]:\n    \"\"\"Get a list of preferred browsers based on the environment variable\n    `RPA_SELENIUM_BROWSER_ORDER` if set.\n\n    The OS dictates the order if no such env var is set.\n    \"\"\"\n    browsers: Optional[List[str]] = _get_browser_order_from_env()\n    if browsers:\n        return browsers\n\n    return _DRIVER_PREFERENCE.get(platform.system(), _DRIVER_PREFERENCE[\"default\"])\n\n\nclass Downloader(WDMHttpClient):\n\n    \"\"\"Custom downloader which disables download progress reporting.\"\"\"\n\n    def get(self, url, **kwargs) -> Response:\n        resp = requests.get(url=url, verify=self._ssl_verify, stream=True, **kwargs)\n        self.validate_response(resp)\n        return resp\n\n\n@contextlib.contextmanager\ndef suppress_logging():\n    \"\"\"Suppress webdriver-manager logging.\"\"\"\n    wdm_log = \"WDM_LOG\"\n    original_value = os.getenv(wdm_log, \"\")\n    try:\n        os.environ[wdm_log] = str(logging.NOTSET)\n        yield\n    finally:\n        os.environ[wdm_log] = original_value\n\n\ndef start(browser: str, service: Optional[Service] = None, **options) -> WebDriver:\n    \"\"\"Start a webdriver with the given options.\"\"\"\n    browser = browser.strip()\n    webdriver_factory = getattr(webdriver, browser, None)\n    if not webdriver_factory:\n        raise ValueError(f\"Unsupported Selenium browser: {browser}\")\n\n    # NOTE: It is recommended to pass a `service` rather than deprecated `options`.\n    driver = webdriver_factory(service=service, **options)\n    return driver\n\n\ndef _to_manager(browser: str, *, root: Path) -> DriverManager:\n    browser = browser.strip()\n    manager_factory = AVAILABLE_DRIVERS.get(browser.lower())\n    if not manager_factory:\n        raise ValueError(\n            f\"Unsupported browser {browser!r} for webdriver download!\"\n            f\" (choose from: {', '.join(SUPPORTED_BROWSERS.values())})\"\n        )\n\n    downloader = Downloader()\n    download_manager = WDMDownloadManager(downloader)\n    manager = manager_factory(path=str(root), download_manager=download_manager)\n    return manager\n\n\ndef _set_executable(path: str) -> None:\n    st = os.stat(path)\n    os.chmod(\n        path,\n        st.st_mode | stat.S_IXOTH | stat.S_IXGRP | stat.S_IEXEC,\n    )\n\n\ndef download(browser: str, root: Path = DRIVER_ROOT) -> Optional[str]:\n    \"\"\"Download a webdriver binary for the given browser and return the path to it.\"\"\"\n    manager = _to_manager(browser, root=root)\n    driver = manager.driver\n    resolved_os = getattr(driver, \"os_type\", driver.get_os_type())\n    os_name = get_os_name()\n    if os_name.lower() not in resolved_os.lower():\n        LOGGER.warning(\n            \"Attempting to download incompatible driver for OS %r on OS %r! Skip\",\n            resolved_os,\n            os_name,\n        )\n        return None  # incompatible driver download attempt\n\n    with suppress_logging():\n        path: str = manager.install()\n    if platform.system() != \"Windows\":\n        _set_executable(path)\n    LOGGER.info(\"Downloaded webdriver to: %s\", path)\n    return path\n", "path": "packages/core/src/RPA/core/webdriver.py"}], "after_files": [{"content": "import contextlib\nimport functools\nimport logging\nimport os\nimport platform\nimport stat\nfrom pathlib import Path\nfrom typing import List, Optional\n\nimport requests\nfrom requests import Response\nfrom selenium import webdriver\nfrom selenium.webdriver.common.service import Service\nfrom selenium.webdriver.remote.webdriver import WebDriver\nfrom webdriver_manager.chrome import ChromeDriverManager\nfrom webdriver_manager.core.download_manager import WDMDownloadManager\nfrom webdriver_manager.core.http import WDMHttpClient\nfrom webdriver_manager.core.manager import DriverManager\nfrom webdriver_manager.core.utils import ChromeType, get_browser_version_from_os\nfrom webdriver_manager.core.utils import os_name as get_os_name\nfrom webdriver_manager.firefox import GeckoDriverManager\nfrom webdriver_manager.microsoft import EdgeChromiumDriverManager, IEDriverManager\nfrom webdriver_manager.opera import OperaDriverManager\n\nfrom RPA.core.robocorp import robocorp_home\n\n\nLOGGER = logging.getLogger(__name__)\n\nDRIVER_ROOT = robocorp_home() / \"webdrivers\"\nAVAILABLE_DRIVERS = {\n    # Driver names taken from `webdrivermanager` and adapted to `webdriver_manager`.\n    \"chrome\": ChromeDriverManager,\n    \"firefox\": GeckoDriverManager,\n    \"gecko\": GeckoDriverManager,\n    \"mozilla\": GeckoDriverManager,\n    # NOTE: Selenium 4 dropped support for Opera.\n    #  (https://github.com/SeleniumHQ/selenium/issues/10835)\n    \"opera\": OperaDriverManager,\n    # NOTE: In Selenium 4 `Edge` is the same with `ChromiumEdge`.\n    \"edge\": EdgeChromiumDriverManager,\n    \"chromiumedge\": EdgeChromiumDriverManager,\n    # NOTE: IE is discontinued and not supported/encouraged anymore.\n    \"ie\": IEDriverManager,\n}\n# Available `WebDriver` classes in Selenium.\nSUPPORTED_BROWSERS = dict(\n    {name: name.capitalize() for name in AVAILABLE_DRIVERS},\n    **{\"chromiumedge\": \"ChromiumEdge\"},\n)\n_DRIVER_PREFERENCE = {\n    \"Windows\": [\"Chrome\", \"Firefox\", \"Edge\"],\n    \"Linux\": [\"Chrome\", \"Firefox\", \"Edge\"],\n    \"Darwin\": [\"Chrome\", \"Firefox\", \"Edge\", \"Safari\"],\n    \"default\": [\"Chrome\", \"Firefox\"],\n}\n\n\ndef _get_browser_order_from_env() -> Optional[List[str]]:\n    browsers: str = os.getenv(\"RPA_SELENIUM_BROWSER_ORDER\", \"\")\n    if browsers:\n        return [browser.strip() for browser in browsers.split(sep=\",\")]\n\n    return None  # meaning there's no env var to control the order\n\n\ndef get_browser_order() -> List[str]:\n    \"\"\"Get a list of preferred browsers based on the environment variable\n    `RPA_SELENIUM_BROWSER_ORDER` if set.\n\n    The OS dictates the order if no such env var is set.\n    \"\"\"\n    browsers: Optional[List[str]] = _get_browser_order_from_env()\n    if browsers:\n        return browsers\n\n    return _DRIVER_PREFERENCE.get(platform.system(), _DRIVER_PREFERENCE[\"default\"])\n\n\nclass Downloader(WDMHttpClient):\n\n    \"\"\"Custom downloader which disables download progress reporting.\"\"\"\n\n    def get(self, url, **kwargs) -> Response:\n        resp = requests.get(url=url, verify=self._ssl_verify, stream=True, **kwargs)\n        self.validate_response(resp)\n        return resp\n\n\n@contextlib.contextmanager\ndef suppress_logging():\n    \"\"\"Suppress webdriver-manager logging.\"\"\"\n    wdm_log = \"WDM_LOG\"\n    original_value = os.getenv(wdm_log, \"\")\n    try:\n        os.environ[wdm_log] = str(logging.NOTSET)\n        yield\n    finally:\n        os.environ[wdm_log] = original_value\n\n\ndef start(browser: str, service: Optional[Service] = None, **options) -> WebDriver:\n    \"\"\"Start a webdriver with the given options.\"\"\"\n    browser = browser.strip()\n    webdriver_factory = getattr(webdriver, browser, None)\n    if not webdriver_factory:\n        raise ValueError(f\"Unsupported Selenium browser: {browser}\")\n\n    # NOTE: It is recommended to pass a `service` rather than deprecated `options`.\n    driver = webdriver_factory(service=service, **options)\n    return driver\n\n\ndef _is_chromium() -> bool:\n    \"\"\"Detects if Chromium is used instead of Chrome no matter the platform.\"\"\"\n    is_browser = lambda browser_type: bool(  # noqa: E731\n        get_browser_version_from_os(browser_type)\n    )\n    return not is_browser(ChromeType.GOOGLE) and is_browser(ChromeType.CHROMIUM)\n\n\ndef _to_manager(browser: str, *, root: Path) -> DriverManager:\n    browser = browser.strip()\n    manager_factory = AVAILABLE_DRIVERS.get(browser.lower())\n    if not manager_factory:\n        raise ValueError(\n            f\"Unsupported browser {browser!r} for webdriver download!\"\n            f\" (choose from: {', '.join(SUPPORTED_BROWSERS.values())})\"\n        )\n\n    if manager_factory == ChromeDriverManager and _is_chromium():\n        manager_factory = functools.partial(\n            manager_factory, chrome_type=ChromeType.CHROMIUM\n        )\n    downloader = Downloader()\n    download_manager = WDMDownloadManager(downloader)\n    manager = manager_factory(path=str(root), download_manager=download_manager)\n    return manager\n\n\ndef _set_executable(path: str) -> None:\n    st = os.stat(path)\n    os.chmod(\n        path,\n        st.st_mode | stat.S_IXOTH | stat.S_IXGRP | stat.S_IEXEC,\n    )\n\n\ndef download(browser: str, root: Path = DRIVER_ROOT) -> Optional[str]:\n    \"\"\"Download a webdriver binary for the given browser and return the path to it.\"\"\"\n    manager = _to_manager(browser, root=root)\n    driver = manager.driver\n    resolved_os = getattr(driver, \"os_type\", driver.get_os_type())\n    os_name = get_os_name()\n    if os_name.lower() not in resolved_os.lower():\n        LOGGER.warning(\n            \"Attempting to download incompatible driver for OS %r on OS %r! Skip\",\n            resolved_os,\n            os_name,\n        )\n        return None  # incompatible driver download attempt\n\n    with suppress_logging():\n        path: str = manager.install()\n    if platform.system() != \"Windows\":\n        _set_executable(path)\n    LOGGER.info(\"Downloaded webdriver to: %s\", path)\n    return path\n", "path": "packages/core/src/RPA/core/webdriver.py"}]}