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+ chrome_version : Optional [str ] = None
43+
44+ if platform .system () == "Windows" :
45+ import browsers
46+
47+ chrome_version = browsers .get ("chrome" )["version" ]
48+ else :
49+ chrome_version = self .get_chrome_version ()
4350
4451 # If Web Driver Manager cannot detect Chrome, it returns None.
4552 if chrome_version is None :
@@ -109,7 +116,18 @@ def _download_chromedriver(
109116 path_to_cached_chrome_driver : str ,
110117 ) -> str :
111118 url = "https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json"
112- response = ChromeDriverManager .send_http_get_request (url ).json ()
119+ response = ChromeDriverManager .send_http_get_request (url )
120+ if response is None :
121+ raise RuntimeError (
122+ "Could not download known-good-versions-with-downloads.json"
123+ )
124+
125+ response = response .json ()
126+ if response is None :
127+ raise RuntimeError (
128+ "Could not parse known-good-versions-with-downloads.json"
129+ )
130+ assert isinstance (response , dict )
113131
114132 matching_versions = [
115133 item
@@ -118,7 +136,7 @@ def _download_chromedriver(
118136 ]
119137
120138 if not matching_versions :
121- raise Exception (
139+ raise RuntimeError (
122140 f"No compatible ChromeDriver found for Chrome version { chrome_major_version } "
123141 )
124142
@@ -142,6 +160,11 @@ def _download_chromedriver(
142160 )
143161 response = ChromeDriverManager .send_http_get_request (driver_url )
144162
163+ if response is None :
164+ raise Exception (
165+ f"Could not download ChromeDriver from { driver_url } "
166+ )
167+
145168 Path (path_to_driver_cache_dir ).mkdir (parents = True , exist_ok = True )
146169 zip_path = os .path .join (path_to_driver_cache_dir , "chromedriver.zip" )
147170 print ( # noqa: T201
@@ -179,6 +202,9 @@ def send_http_get_request(url: str) -> Response:
179202 f"html2print: "
180203 f"failed to get response for URL: { url } with error: { last_error } "
181204 )
205+ raise RuntimeError (
206+ f"GET request failed after 3 attempts: { url } "
207+ ) from last_error
182208
183209 @staticmethod
184210 def get_chrome_version () -> Optional [str ]:
@@ -328,14 +354,24 @@ def create_webdriver(
328354 service = Service (path_to_chrome )
329355
330356 webdriver_options = Options ()
357+ # Workaround for Windows: chrome is not typically found in the PATH, so need to supply the exact binary location manually
358+ if platform .system () == "Windows" :
359+ import browsers
360+
361+ chrome_binary_location = browsers .get ("chrome" )["path" ]
362+ webdriver_options .binary_location = chrome_binary_location
331363 webdriver_options .add_argument ("start-maximized" )
332364 webdriver_options .add_argument ("disable-infobars" )
333365 # Doesn't seem to be needed.
334366 # webdriver_options.add_argument('--disable-gpu') # noqa: ERA001
335367 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
368+ # Use --headless=new, as it seems to be more stable on Windows (available since Chrome 109).
369+ # see https://www.selenium.dev/blog/2023/headless-is-going-away/
370+ webdriver_options .add_argument ("--headless=new" )
371+ # Docker disables some syscalls that are required for Chrome's sandbox to work.
372+ # https://stackoverflow.com/questions/68855734/how-to-setup-chrome-sandbox-on-docker-container.
373+ # See also https://github.com/SeleniumHQ/selenium/issues/15327#issuecomment-2689287561.
374+ # We prefer isolation of the container over isolation of tabs within Chrome, and thus disable the sandbox.
339375 webdriver_options .add_argument ("--no-sandbox" )
340376
341377 # The Chrome option --disable-dev-shm-usage disables the use of /dev/shm
0 commit comments