4
4
using System . Collections . Immutable ;
5
5
using System . Diagnostics ;
6
6
using System . Diagnostics . CodeAnalysis ;
7
+ using System . Runtime . CompilerServices ;
7
8
using Microsoft . Build . Graph ;
8
9
using Microsoft . DotNet . HotReload ;
9
10
using Microsoft . Extensions . Logging ;
10
11
11
12
namespace Microsoft . DotNet . Watch ;
12
13
13
- internal sealed class BrowserLauncher ( ILogger logger , EnvironmentOptions environmentOptions )
14
+ internal sealed class BrowserLauncher ( ILogger logger , IProcessOutputReporter processOutputReporter , EnvironmentOptions environmentOptions )
14
15
{
15
16
// interlocked
16
17
private ImmutableHashSet < ProjectInstanceId > _browserLaunchAttempted = [ ] ;
@@ -61,18 +62,13 @@ public static string GetLaunchUrl(string? profileLaunchUrl, string outputLaunchU
61
62
62
63
private void LaunchBrowser ( string launchUrl , AbstractBrowserRefreshServer ? server )
63
64
{
64
- var fileName = launchUrl ;
65
+ var ( fileName , arg , useShellExecute ) = environmentOptions . BrowserPath is { } browserPath
66
+ ? ( browserPath , launchUrl , false )
67
+ : ( launchUrl , null , true ) ;
65
68
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 ) ;
72
70
73
- logger . LogDebug ( "Launching browser: {FileName} {Args}" , fileName , args ) ;
74
-
75
- if ( environmentOptions . TestFlags != TestFlags . None )
71
+ if ( environmentOptions . TestFlags != TestFlags . None && environmentOptions . BrowserPath == null )
76
72
{
77
73
if ( environmentOptions . TestFlags . HasFlag ( TestFlags . MockBrowser ) )
78
74
{
@@ -83,29 +79,23 @@ private void LaunchBrowser(string launchUrl, AbstractBrowserRefreshServer? serve
83
79
return ;
84
80
}
85
81
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 ( )
87
88
{
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 ,
91
93
} ;
92
94
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 } )
107
97
{
108
- logger . LogDebug ( "Failed to launch a browser: {Message} ", e . Message ) ;
98
+ logger . LogWarning ( "Unable to launch the browser. Url '{Url}'. ", launchUrl ) ;
109
99
}
110
100
}
111
101
0 commit comments