11using System ;
22using System . IO ;
3+ using System . Linq ;
34using System . Net ;
45using System . Runtime . InteropServices ;
6+ using WebDriverManager . Clients ;
57using WebDriverManager . Helpers ;
8+ using WebDriverManager . Models . Chrome ;
69
710namespace WebDriverManager . DriverConfigs . Impl
811{
912 public class ChromeConfig : IDriverConfig
1013 {
1114 private const string BaseVersionPatternUrl = "https://chromedriver.storage.googleapis.com/<version>/" ;
12- private const string LatestReleaseVersionUrl = "https://chromedriver.storage.googleapis.com/LATEST_RELEASE " ;
15+ private const string ExactReleaseVersionPatternUrl = "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_<version> " ;
1316
14- private const string ExactReleaseVersionPatternUrl =
15- "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_<version>" ;
17+ /// <summary>
18+ /// The minimum version required to download chrome drivers from Chrome for Testing API's
19+ /// </summary>
20+ private static readonly Version MinChromeForTestingDriverVersion = new Version ( "115.0.5763.0" ) ;
21+
22+ /// <summary>
23+ /// The minimum version of chrome driver required to reference download URLs via the "arm64" extension
24+ /// </summary>
25+ private static readonly Version MinArm64ExtensionVersion = new Version ( "106.0.5249.61" ) ;
26+
27+ private ChromeVersionInfo _chromeVersionInfo ;
28+ private string _chromeVersion ;
1629
1730 public virtual string GetName ( )
1831 {
@@ -31,23 +44,14 @@ public virtual string GetUrl64()
3144
3245 private string GetUrl ( )
3346 {
34- #if NETSTANDARD
35- if ( RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) )
47+ // Handle newer versions of chrome driver only being available for download via the Chrome for Testing API's
48+ // whilst retaining backwards compatibility for older versions of chrome/chrome driver.
49+ if ( _chromeVersionInfo != null )
3650 {
37- var architectureExtension =
38- RuntimeInformation . ProcessArchitecture == System . Runtime . InteropServices . Architecture . Arm64
39- ? "_arm64"
40- : "64" ;
41- return $ "{ BaseVersionPatternUrl } chromedriver_mac{ architectureExtension } .zip";
51+ return GetUrlFromChromeForTestingApi ( ) ;
4252 }
4353
44- if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) )
45- {
46- return $ "{ BaseVersionPatternUrl } chromedriver_linux64.zip";
47- }
48- #endif
49-
50- return $ "{ BaseVersionPatternUrl } chromedriver_win32.zip";
54+ return GetUrlFromChromeStorage ( ) ;
5155 }
5256
5357 public virtual string GetBinaryName ( )
@@ -63,10 +67,49 @@ public virtual string GetBinaryName()
6367
6468 public virtual string GetLatestVersion ( )
6569 {
66- return GetLatestVersion ( LatestReleaseVersionUrl ) ;
70+ var chromeReleases = ChromeForTestingClient . GetLastKnownGoodVersions ( ) ;
71+ var chromeStable = chromeReleases . Channels . Stable ;
72+
73+ _chromeVersionInfo = new ChromeVersionInfo
74+ {
75+ Downloads = chromeStable . Downloads
76+ } ;
77+
78+ return chromeStable . Version ;
6779 }
6880
69- private static string GetLatestVersion ( string url )
81+ public virtual string GetMatchingBrowserVersion ( )
82+ {
83+ var rawChromeBrowserVersion = GetRawBrowserVersion ( ) ;
84+ if ( string . IsNullOrEmpty ( rawChromeBrowserVersion ) )
85+ {
86+ throw new Exception ( "Not able to get chrome version or not installed" ) ;
87+ }
88+
89+ var chromeVersion = VersionHelper . GetVersionWithoutRevision ( rawChromeBrowserVersion ) ;
90+
91+ // Handle downloading versions of the chrome webdriver less than what's supported by the Chrome for Testing known good versions API
92+ // See https://googlechromelabs.github.io/chrome-for-testing for more info
93+ var matchedVersion = new Version ( rawChromeBrowserVersion ) ;
94+ if ( matchedVersion < MinChromeForTestingDriverVersion )
95+ {
96+ var url = ExactReleaseVersionPatternUrl . Replace ( "<version>" , chromeVersion ) ;
97+ _chromeVersion = GetVersionFromChromeStorage ( url ) ;
98+ }
99+ else
100+ {
101+ _chromeVersion = GetVersionFromChromeForTestingApi ( chromeVersion ) . Version ;
102+ }
103+
104+ return _chromeVersion ;
105+ }
106+
107+ /// <summary>
108+ /// Retrieves a chrome driver version string from https://chromedriver.storage.googleapis.com
109+ /// </summary>
110+ /// <param name="url">The request URL</param>
111+ /// <returns>A chrome driver version string</returns>
112+ private static string GetVersionFromChromeStorage ( string url )
70113 {
71114 var uri = new Uri ( url ) ;
72115 var webRequest = WebRequest . Create ( uri ) ;
@@ -84,17 +127,85 @@ private static string GetLatestVersion(string url)
84127 }
85128 }
86129
87- public virtual string GetMatchingBrowserVersion ( )
130+ /// <summary>
131+ /// Retrieves a download URL for a chrome driver from the https://chromedriver.storage.googleapis.com API's
132+ /// </summary>
133+ /// <returns>A chrome driver download URL</returns>
134+ private string GetUrlFromChromeStorage ( )
88135 {
89- var rawChromeBrowserVersion = GetRawBrowserVersion ( ) ;
90- if ( string . IsNullOrEmpty ( rawChromeBrowserVersion ) )
136+ #if NETSTANDARD
137+ if ( RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) )
91138 {
92- throw new Exception ( "Not able to get chrome version or not installed" ) ;
139+ // Handle older versions of chrome driver arm64 builds that are tagged with 64_m1 instead of arm64.
140+ // See: https://chromedriver.storage.googleapis.com/index.html?path=106.0.5249.21/
141+ var useM1Prefix = new Version ( _chromeVersion ) < MinArm64ExtensionVersion ;
142+ var armArchitectureExtension = useM1Prefix
143+ ? "64_m1"
144+ : "_arm64" ;
145+
146+ var architectureExtension = RuntimeInformation . ProcessArchitecture == System . Runtime . InteropServices . Architecture . Arm64
147+ ? armArchitectureExtension
148+ : "64" ;
149+
150+ return $ "{ BaseVersionPatternUrl } chromedriver_mac{ architectureExtension } .zip";
93151 }
94152
95- var chromeBrowserVersion = VersionHelper . GetVersionWithoutRevision ( rawChromeBrowserVersion ) ;
96- var url = ExactReleaseVersionPatternUrl . Replace ( "<version>" , chromeBrowserVersion ) ;
97- return GetLatestVersion ( url ) ;
153+ if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) )
154+ {
155+ return $ "{ BaseVersionPatternUrl } chromedriver_linux64.zip";
156+ }
157+ #endif
158+
159+ return $ "{ BaseVersionPatternUrl } chromedriver_win32.zip";
160+ }
161+
162+ /// <summary>
163+ /// Retrieves a chrome driver version string from https://googlechromelabs.github.io/chrome-for-testing
164+ /// </summary>
165+ /// <param name="version">The desired version to download</param>
166+ /// <returns>Chrome driver version info (version number, revision number, download URLs)</returns>
167+ private ChromeVersionInfo GetVersionFromChromeForTestingApi ( string noRevisionVersion )
168+ {
169+ var knownGoodVersions = ChromeForTestingClient . GetKnownGoodVersionsWithDownloads ( ) ;
170+
171+ // Pull latest patch version
172+ _chromeVersionInfo = knownGoodVersions . Versions . LastOrDefault (
173+ cV => cV . Version . Contains ( noRevisionVersion )
174+ ) ;
175+
176+ return _chromeVersionInfo ;
177+ }
178+
179+ /// <summary>
180+ /// Retrieves a chrome driver download URL from Chrome for Testing API's
181+ /// </summary>
182+ /// <returns>A chrome driver download URL</returns>
183+ private string GetUrlFromChromeForTestingApi ( )
184+ {
185+ var platform = "win32" ;
186+
187+ #if NETSTANDARD
188+ if ( RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) )
189+ {
190+ platform = RuntimeInformation . ProcessArchitecture == System . Runtime . InteropServices . Architecture . Arm64
191+ ? "mac-arm64"
192+ : "mac-x64" ;
193+ }
194+ else if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) )
195+ {
196+ platform = "linux64" ;
197+ }
198+ else if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
199+ {
200+ platform = RuntimeInformation . ProcessArchitecture == System . Runtime . InteropServices . Architecture . X64
201+ ? "win64"
202+ : "win32" ;
203+ }
204+ #endif
205+ var result = _chromeVersionInfo . Downloads . ChromeDriver
206+ . FirstOrDefault ( driver => driver . Platform == platform ) ;
207+
208+ return result . Url ;
98209 }
99210
100211 private string GetRawBrowserVersion ( )
0 commit comments