3939
4040class ChromeDriverManager :
4141 def get_chrome_driver (self , path_to_cache_dir : str ) -> str :
42- chrome_version : Optional [str ] = self .get_chrome_version ()
42+ if platform .system () == "Windows" :
43+ import browsers
44+
45+ chrome_version : Optional [str ] = browsers .get ("chrome" )["version" ]
46+ else :
47+ chrome_version : Optional [str ] = self .get_chrome_version ()
4348
4449 # If Web Driver Manager cannot detect Chrome, it returns None.
4550 if chrome_version is None :
@@ -109,7 +114,18 @@ def _download_chromedriver(
109114 path_to_cached_chrome_driver : str ,
110115 ) -> str :
111116 url = "https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json"
112- response = ChromeDriverManager .send_http_get_request (url ).json ()
117+ response = ChromeDriverManager .send_http_get_request (url )
118+ if response is None :
119+ raise RuntimeError (
120+ "Could not download known-good-versions-with-downloads.json"
121+ )
122+
123+ response = response .json ()
124+ if response is None :
125+ raise RuntimeError (
126+ "Could not parse known-good-versions-with-downloads.json"
127+ )
128+ assert isinstance (response , dict )
113129
114130 matching_versions = [
115131 item
@@ -118,7 +134,7 @@ def _download_chromedriver(
118134 ]
119135
120136 if not matching_versions :
121- raise Exception (
137+ raise RuntimeError (
122138 f"No compatible ChromeDriver found for Chrome version { chrome_major_version } "
123139 )
124140
@@ -142,6 +158,11 @@ def _download_chromedriver(
142158 )
143159 response = ChromeDriverManager .send_http_get_request (driver_url )
144160
161+ if response is None :
162+ raise Exception (
163+ f"Could not download ChromeDriver from { driver_url } "
164+ )
165+
145166 Path (path_to_driver_cache_dir ).mkdir (parents = True , exist_ok = True )
146167 zip_path = os .path .join (path_to_driver_cache_dir , "chromedriver.zip" )
147168 print ( # noqa: T201
@@ -179,6 +200,9 @@ def send_http_get_request(url: str) -> Response:
179200 f"html2print: "
180201 f"failed to get response for URL: { url } with error: { last_error } "
181202 )
203+ raise RuntimeError (
204+ f"GET request failed after 3 attempts: { url } "
205+ ) from last_error
182206
183207 @staticmethod
184208 def get_chrome_version () -> Optional [str ]:
@@ -328,14 +352,24 @@ def create_webdriver(
328352 service = Service (path_to_chrome )
329353
330354 webdriver_options = Options ()
355+ # Workaround for Windows: chrome is not typically found in the PATH, so need to supply the exact binary location manually
356+ if platform .system () == "Windows" :
357+ import browsers
358+
359+ chrome_binary_location = browsers .get ("chrome" )["path" ]
360+ webdriver_options .binary_location = chrome_binary_location
331361 webdriver_options .add_argument ("start-maximized" )
332362 webdriver_options .add_argument ("disable-infobars" )
333363 # Doesn't seem to be needed.
334364 # webdriver_options.add_argument('--disable-gpu') # noqa: ERA001
335365 webdriver_options .add_argument ("--disable-extensions" )
336- webdriver_options .add_argument ("--headless=chrome" )
337- # FIXME: This is not nice but otherwise it does not work in Ubuntu 24-based Docker image.
338- # https://github.com/SeleniumHQ/selenium/issues/15327#issuecomment-2689287561
366+ # Use --headless=new, as it seems to be more stable on Windows (available since Chrome 109).
367+ # see https://www.selenium.dev/blog/2023/headless-is-going-away/
368+ webdriver_options .add_argument ("--headless=new" )
369+ # Docker disables some syscalls that are required for Chrome's sandbox to work.
370+ # https://stackoverflow.com/questions/68855734/how-to-setup-chrome-sandbox-on-docker-container.
371+ # See also https://github.com/SeleniumHQ/selenium/issues/15327#issuecomment-2689287561.
372+ # We prefer isolation of the container over isolation of tabs within Chrome, and thus disable the sandbox.
339373 webdriver_options .add_argument ("--no-sandbox" )
340374
341375 # The Chrome option --disable-dev-shm-usage disables the use of /dev/shm
0 commit comments