44using  System . Collections . Immutable ; 
55using  System . Diagnostics ; 
66using  System . Diagnostics . CodeAnalysis ; 
7+ using  System . Runtime . CompilerServices ; 
78using  Microsoft . Build . Graph ; 
89using  Microsoft . DotNet . HotReload ; 
910using  Microsoft . Extensions . Logging ; 
1011
1112namespace  Microsoft . DotNet . Watch ; 
1213
13- internal  sealed  class  BrowserLauncher ( ILogger  logger ,  EnvironmentOptions  environmentOptions ) 
14+ internal  sealed  class  BrowserLauncher ( ILogger  logger ,  IProcessOutputReporter   processOutputReporter ,   EnvironmentOptions  environmentOptions ) 
1415{ 
1516    // interlocked 
1617    private  ImmutableHashSet < ProjectInstanceId >  _browserLaunchAttempted  =  [ ] ; 
@@ -61,18 +62,13 @@ public static string GetLaunchUrl(string? profileLaunchUrl, string outputLaunchU
6162
6263    private  void  LaunchBrowser ( string  launchUrl ,  BrowserRefreshServer ?  server ) 
6364    { 
64-         var  fileName  =  launchUrl ; 
65+         var  ( fileName ,  arg ,  useShellExecute )  =  environmentOptions . BrowserPath  is  {  }  browserPath 
66+             ?  ( browserPath ,  launchUrl ,  false ) 
67+             :  ( launchUrl ,  null ,  true ) ; 
6568
66-         var  args  =  string . Empty ; 
67-         if  ( environmentOptions . BrowserPath  is  {  }  browserPath ) 
68-         { 
69-             args  =  fileName ; 
70-             fileName  =  browserPath ; 
71-         } 
69+         logger . Log ( MessageDescriptor . LaunchingBrowser ,  fileName ,  arg ) ; 
7270
73-         logger . Log ( MessageDescriptor . LaunchingBrowser ,  fileName ,  args ) ; 
74- 
75-         if  ( environmentOptions . TestFlags  !=  TestFlags . None ) 
71+         if  ( environmentOptions . TestFlags  !=  TestFlags . None  &&  environmentOptions . BrowserPath  ==  null ) 
7672        { 
7773            if  ( environmentOptions . TestFlags . HasFlag ( TestFlags . MockBrowser ) ) 
7874            { 
@@ -83,29 +79,23 @@ private void LaunchBrowser(string launchUrl, BrowserRefreshServer? server)
8379            return ; 
8480        } 
8581
86-         var  info  =  new  ProcessStartInfo 
82+         // dotnet-watch, by default, relies on URL file association to launch browsers. On Windows and MacOS, this works fairly well 
83+         // where URLs are associated with the default browser. On Linux, this is a bit murky. 
84+         // From emperical observation, it's noted that failing to launch a browser results in either Process.Start returning a null-value 
85+         // or for the process to have immediately exited. 
86+         // We can use this to provide a helpful message. 
87+         var  processSpec  =  new  ProcessSpec ( ) 
8788        { 
88-             FileName  =  fileName , 
89-             Arguments  =  args , 
90-             UseShellExecute  =  true , 
89+             Executable  =  fileName , 
90+             Arguments  =  arg  !=  null  ?  [ arg ]  :  [ ] , 
91+             UseShellExecute  =  useShellExecute , 
92+             OnOutput  =  environmentOptions . TestFlags . HasFlag ( TestFlags . RedirectBrowserOutput )  ?  processOutputReporter . ReportOutput  :  null , 
9193        } ; 
9294
93-         try 
94-         { 
95-             using  var  browserProcess  =  Process . Start ( info ) ; 
96-             if  ( browserProcess  is  null  or {  HasExited :  true  } ) 
97-             { 
98-                 // dotnet-watch, by default, relies on URL file association to launch browsers. On Windows and MacOS, this works fairly well 
99-                 // where URLs are associated with the default browser. On Linux, this is a bit murky. 
100-                 // From emperical observation, it's noted that failing to launch a browser results in either Process.Start returning a null-value 
101-                 // or for the process to have immediately exited. 
102-                 // We can use this to provide a helpful message. 
103-                 logger . LogInformation ( "Unable to launch the browser. Url '{Url}'." ,  launchUrl ) ; 
104-             } 
105-         } 
106-         catch  ( Exception  e ) 
95+         using  var  browserProcess  =  ProcessRunner . TryStartProcess ( processSpec ,  logger ) ; 
96+         if  ( browserProcess  is  null  or {  HasExited :  true  } ) 
10797        { 
108-             logger . LogDebug ( "Failed  to launch a  browser: {Message} ",  e . Message ) ; 
98+             logger . LogWarning ( "Unable  to launch the  browser. Url '{Url}'. ",  launchUrl ) ; 
10999        } 
110100    } 
111101
0 commit comments