1313from time import sleep
1414from typing import Dict , List , Optional
1515
16+ import browsers
1617import requests
17- from requests import Response
18+ from requests import RequestException , Response
1819from selenium import webdriver
1920from selenium .webdriver .chrome .options import Options
2021from selenium .webdriver .chrome .service import Service
21- from webdriver_manager .core .os_manager import ChromeType , OperationSystemManager
2222
2323__version__ = "0.0.15"
2424
3939
4040
4141class ChromeDriverManager :
42- def get_chrome_driver (self , path_to_cache_dir : str ):
43- chrome_version = self .get_chrome_version ()
44-
42+ def get_chrome_driver (self , chrome_version : str , path_to_cache_dir : str ):
4543 # If Web Driver Manager cannot detect Chrome, it returns None.
4644 if chrome_version is None :
4745 raise RuntimeError (
@@ -104,13 +102,24 @@ def get_chrome_driver(self, path_to_cache_dir: str):
104102
105103 @staticmethod
106104 def _download_chromedriver (
107- chrome_major_version ,
105+ chrome_major_version : str ,
108106 os_type : str ,
109- path_to_driver_cache_dir ,
110- path_to_cached_chrome_driver ,
111- ):
107+ path_to_driver_cache_dir : str ,
108+ path_to_cached_chrome_driver : str ,
109+ ) -> str :
112110 url = "https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json"
113- response = ChromeDriverManager .send_http_get_request (url ).json ()
111+ response = ChromeDriverManager .send_http_get_request (url )
112+ if response is None :
113+ raise RuntimeError (
114+ "Could not download known-good-versions-with-downloads.json"
115+ )
116+
117+ response = response .json ()
118+ if response is None :
119+ raise RuntimeError (
120+ "Could not parse known-good-versions-with-downloads.json"
121+ )
122+ assert isinstance (response , dict )
114123
115124 matching_versions = [
116125 item
@@ -143,6 +152,11 @@ def _download_chromedriver(
143152 )
144153 response = ChromeDriverManager .send_http_get_request (driver_url )
145154
155+ if response is None :
156+ raise Exception (
157+ f"Could not download ChromeDriver from { driver_url } "
158+ )
159+
146160 Path (path_to_driver_cache_dir ).mkdir (parents = True , exist_ok = True )
147161 zip_path = os .path .join (path_to_driver_cache_dir , "chromedriver.zip" )
148162 print ( # noqa: T201
@@ -180,9 +194,12 @@ def send_http_get_request(url, params=None, **kwargs) -> Response:
180194 f"html2print: "
181195 f"failed to get response for URL: { url } with error: { last_error } "
182196 )
197+ raise RequestException (
198+ f"GET request failed after 3 attempts: { url } "
199+ ) from last_error
183200
184201 @staticmethod
185- def get_chrome_version () :
202+ def get_browser_info () -> browsers . Browser :
186203 # Special case: GitHub Actions macOS CI machines have both
187204 # Google Chrome for Testing and normal Google Chrome installed, and
188205 # sometimes their versions are of different major version families.
@@ -216,17 +233,24 @@ def get_chrome_version():
216233 f"html2print: Google Chrome for Testing Version: { chrome_version } "
217234 )
218235
219- return chrome_version
236+ return browsers .Browser (
237+ browser_type = "chrome-for-testing" ,
238+ path = chrome_path ,
239+ version = chrome_version ,
240+ display_name = "Google Chrome for Testing" ,
241+ )
220242 except FileNotFoundError :
221243 print ("html2print: Chrome for Testing not available." ) # noqa: T201
222244 except Exception as e :
223245 print ( # noqa: T201
224246 f"html2print: Error getting Google Chrome for Testing version: { e } "
225247 )
248+ else :
249+ chrome_browser_info = browsers .get ("chrome" ) or browsers .get (
250+ "chromium"
251+ )
226252
227- os_manager = OperationSystemManager (os_type = None )
228- version = os_manager .get_browser_version_from_os (ChromeType .GOOGLE )
229- return version
253+ return chrome_browser_info
230254
231255
232256def get_inches_from_millimeters (mm : float ) -> float :
@@ -305,34 +329,44 @@ class Done(Exception):
305329
306330
307331def create_webdriver (
332+ browser_info : browsers .Browser ,
308333 chromedriver : Optional [str ],
309334 path_to_cache_dir : str ,
310335 page_load_timeout : int ,
311336 debug : bool = False ,
312337) -> webdriver .Chrome :
313338 print ("html2print: creating ChromeDriver service." , flush = True ) # noqa: T201
339+
314340 if chromedriver is None :
315- path_to_chrome = ChromeDriverManager ().get_chrome_driver (
316- path_to_cache_dir
341+ path_to_chrome_driver = ChromeDriverManager ().get_chrome_driver (
342+ chrome_version = browser_info ["version" ],
343+ path_to_cache_dir = path_to_cache_dir ,
317344 )
318345 else :
319- path_to_chrome = chromedriver
320- print (f"html2print: ChromeDriver available at path: { path_to_chrome } " ) # noqa: T201
346+ path_to_chrome_driver = chromedriver
347+ print ( # noqa: T201
348+ f"html2print: ChromeDriver available at path: { path_to_chrome_driver } "
349+ )
321350
322351 if debug :
323352 service = Service (
324- path_to_chrome , log_output = PATH_TO_CHROME_DRIVER_DEBUG_LOG
353+ path_to_chrome_driver , log_output = PATH_TO_CHROME_DRIVER_DEBUG_LOG
325354 )
326355 else :
327- service = Service (path_to_chrome )
356+ service = Service (path_to_chrome_driver )
328357
329358 webdriver_options = Options ()
359+ # Workaround for Windows: chrome is not typically found in the PATH, so need to supply the exact binary location manually
360+ if platform .system () == "Windows" :
361+ webdriver_options .binary_location = browser_info ["path" ]
330362 webdriver_options .add_argument ("start-maximized" )
331363 webdriver_options .add_argument ("disable-infobars" )
332364 # Doesn't seem to be needed.
333365 # webdriver_options.add_argument('--disable-gpu') # noqa: ERA001
334366 webdriver_options .add_argument ("--disable-extensions" )
335- webdriver_options .add_argument ("--headless=chrome" )
367+ # Use --headless=new, as it seems to be more stable on Windows (available since Chrome 109).
368+ # see https://www.selenium.dev/blog/2023/headless-is-going-away/
369+ webdriver_options .add_argument ("--headless=new" )
336370 # FIXME: This is not nice but otherwise it does not work in Ubuntu 24-based Docker image.
337371 # https://github.com/SeleniumHQ/selenium/issues/15327#issuecomment-2689287561
338372 webdriver_options .add_argument ("--no-sandbox" )
@@ -441,16 +475,31 @@ def main():
441475
442476 args = parser .parse_args ()
443477
478+ # Look for Chrome on the System...
479+ browser_info = ChromeDriverManager .get_browser_info ()
480+
444481 path_to_cache_dir : str
445482 if args .command == "get_driver" :
446483 path_to_cache_dir = (
447484 args .cache_dir if args .cache_dir is not None else DEFAULT_CACHE_DIR
448485 )
449486
450- path_to_chrome = ChromeDriverManager ().get_chrome_driver (
451- path_to_cache_dir
487+ path_to_chrome_browser = browser_info ["path" ]
488+ print ( # noqa: T201
489+ f"html2print: Chrome available at path: { path_to_chrome_browser } "
490+ )
491+ chrome_version = browser_info ["version" ]
492+ print ( # noqa: T201
493+ f"html2print: Chrome version : { chrome_version } "
494+ )
495+
496+ path_to_chrome_driver = ChromeDriverManager ().get_chrome_driver (
497+ chrome_version = browser_info ["version" ],
498+ path_to_cache_dir = path_to_cache_dir ,
499+ )
500+ print ( # noqa: T201
501+ f"html2print: ChromeDriver available at path: { path_to_chrome_driver } "
452502 )
453- print (f"html2print: ChromeDriver available at path: { path_to_chrome } " ) # noqa: T201
454503 sys .exit (0 )
455504
456505 elif args .command == "print" :
@@ -462,6 +511,7 @@ def main():
462511 args .cache_dir if args .cache_dir is not None else DEFAULT_CACHE_DIR
463512 )
464513 driver = create_webdriver (
514+ browser_info ,
465515 args .chromedriver ,
466516 path_to_cache_dir ,
467517 page_load_timeout ,
0 commit comments