Skip to content

Commit 47f54ac

Browse files
authored
feat(tools): Arbitrary browser flags (closes #65575) (#104935)
1 parent 08779f0 commit 47f54ac

14 files changed

+194
-34
lines changed

packages/flutter_tools/lib/src/commands/drive.dart

+1
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ class DriveCommand extends RunCommandBase {
279279
packageConfig,
280280
chromeBinary: stringArgDeprecated('chrome-binary'),
281281
headless: boolArgDeprecated('headless'),
282+
webBrowserFlags: stringsArg(FlutterOptions.kWebBrowserFlag),
282283
browserDimension: stringArgDeprecated('browser-dimension')!.split(','),
283284
browserName: stringArgDeprecated('browser-name'),
284285
driverPort: stringArgDeprecated('driver-port') != null

packages/flutter_tools/lib/src/commands/run.dart

+8-3
Original file line numberDiff line numberDiff line change
@@ -202,9 +202,12 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
202202
@protected
203203
Future<DebuggingOptions> createDebuggingOptions(bool webMode) async {
204204
final BuildInfo buildInfo = await getBuildInfo();
205-
final int? browserDebugPort = featureFlags.isWebEnabled && argResults!.wasParsed('web-browser-debug-port')
205+
final int? webBrowserDebugPort = featureFlags.isWebEnabled && argResults!.wasParsed('web-browser-debug-port')
206206
? int.parse(stringArgDeprecated('web-browser-debug-port')!)
207207
: null;
208+
final List<String> webBrowserFlags = featureFlags.isWebEnabled
209+
? stringsArg(FlutterOptions.kWebBrowserFlag)
210+
: const <String>[];
208211
if (buildInfo.mode.isRelease) {
209212
return DebuggingOptions.disabled(
210213
buildInfo,
@@ -216,7 +219,8 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
216219
webUseSseForInjectedClient: featureFlags.isWebEnabled && stringArgDeprecated('web-server-debug-injected-client-protocol') == 'sse',
217220
webEnableExposeUrl: featureFlags.isWebEnabled && boolArgDeprecated('web-allow-expose-url'),
218221
webRunHeadless: featureFlags.isWebEnabled && boolArgDeprecated('web-run-headless'),
219-
webBrowserDebugPort: browserDebugPort,
222+
webBrowserDebugPort: webBrowserDebugPort,
223+
webBrowserFlags: webBrowserFlags,
220224
enableImpeller: enableImpeller,
221225
uninstallFirst: uninstallFirst,
222226
);
@@ -253,7 +257,8 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
253257
webUseSseForInjectedClient: featureFlags.isWebEnabled && stringArgDeprecated('web-server-debug-injected-client-protocol') == 'sse',
254258
webEnableExposeUrl: featureFlags.isWebEnabled && boolArgDeprecated('web-allow-expose-url'),
255259
webRunHeadless: featureFlags.isWebEnabled && boolArgDeprecated('web-run-headless'),
256-
webBrowserDebugPort: browserDebugPort,
260+
webBrowserDebugPort: webBrowserDebugPort,
261+
webBrowserFlags: webBrowserFlags,
257262
webEnableExpressionEvaluation: featureFlags.isWebEnabled && boolArgDeprecated('web-enable-expression-evaluation'),
258263
webLaunchUrl: featureFlags.isWebEnabled ? stringArgDeprecated('web-launch-url') : null,
259264
vmserviceOutFile: stringArgDeprecated('vmservice-out-file'),

packages/flutter_tools/lib/src/device.dart

+8
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,7 @@ class DebuggingOptions {
785785
this.webUseSseForInjectedClient = true,
786786
this.webRunHeadless = false,
787787
this.webBrowserDebugPort,
788+
this.webBrowserFlags = const <String>[],
788789
this.webEnableExpressionEvaluation = false,
789790
this.webLaunchUrl,
790791
this.vmserviceOutFile,
@@ -805,6 +806,7 @@ class DebuggingOptions {
805806
this.webUseSseForInjectedClient = true,
806807
this.webRunHeadless = false,
807808
this.webBrowserDebugPort,
809+
this.webBrowserFlags = const <String>[],
808810
this.webLaunchUrl,
809811
this.cacheSkSL = false,
810812
this.traceAllowlist,
@@ -871,6 +873,7 @@ class DebuggingOptions {
871873
required this.webUseSseForInjectedClient,
872874
required this.webRunHeadless,
873875
required this.webBrowserDebugPort,
876+
required this.webBrowserFlags,
874877
required this.webEnableExpressionEvaluation,
875878
required this.webLaunchUrl,
876879
required this.vmserviceOutFile,
@@ -930,6 +933,9 @@ class DebuggingOptions {
930933
/// The port the browser should use for its debugging protocol.
931934
final int? webBrowserDebugPort;
932935

936+
/// Arbitrary browser flags.
937+
final List<String> webBrowserFlags;
938+
933939
/// Enable expression evaluation for web target.
934940
final bool webEnableExpressionEvaluation;
935941

@@ -983,6 +989,7 @@ class DebuggingOptions {
983989
'webUseSseForInjectedClient': webUseSseForInjectedClient,
984990
'webRunHeadless': webRunHeadless,
985991
'webBrowserDebugPort': webBrowserDebugPort,
992+
'webBrowserFlags': webBrowserFlags,
986993
'webEnableExpressionEvaluation': webEnableExpressionEvaluation,
987994
'webLaunchUrl': webLaunchUrl,
988995
'vmserviceOutFile': vmserviceOutFile,
@@ -1027,6 +1034,7 @@ class DebuggingOptions {
10271034
webUseSseForInjectedClient: (json['webUseSseForInjectedClient'] as bool?)!,
10281035
webRunHeadless: (json['webRunHeadless'] as bool?)!,
10291036
webBrowserDebugPort: json['webBrowserDebugPort'] as int?,
1037+
webBrowserFlags: ((json['webBrowserFlags'] as List<dynamic>?)?.cast<String>())!,
10301038
webEnableExpressionEvaluation: (json['webEnableExpressionEvaluation'] as bool?)!,
10311039
webLaunchUrl: json['webLaunchUrl'] as String?,
10321040
vmserviceOutFile: json['vmserviceOutFile'] as String?,

packages/flutter_tools/lib/src/drive/drive_service.dart

+2
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ abstract class DriverService {
9797
String? browserName,
9898
bool? androidEmulator,
9999
int? driverPort,
100+
List<String> webBrowserFlags,
100101
List<String>? browserDimension,
101102
String? profileMemory,
102103
});
@@ -254,6 +255,7 @@ class FlutterDriverService extends DriverService {
254255
String? browserName,
255256
bool? androidEmulator,
256257
int? driverPort,
258+
List<String> webBrowserFlags = const <String>[],
257259
List<String>? browserDimension,
258260
String? profileMemory,
259261
}) async {

packages/flutter_tools/lib/src/drive/web_driver_service.dart

+21-5
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ class WebDriverService extends DriverService {
136136
String? browserName,
137137
bool? androidEmulator,
138138
int? driverPort,
139+
List<String> webBrowserFlags = const <String>[],
139140
List<String>? browserDimension,
140141
String? profileMemory,
141142
}) async {
@@ -144,7 +145,12 @@ class WebDriverService extends DriverService {
144145
try {
145146
webDriver = await async_io.createDriver(
146147
uri: Uri.parse('http://localhost:$driverPort/'),
147-
desired: getDesiredCapabilities(browser, headless, chromeBinary),
148+
desired: getDesiredCapabilities(
149+
browser,
150+
headless,
151+
webBrowserFlags: webBrowserFlags,
152+
chromeBinary: chromeBinary,
153+
),
148154
);
149155
} on SocketException catch (error) {
150156
_logger.printTrace('$error');
@@ -234,10 +240,15 @@ enum Browser {
234240
safari,
235241
}
236242

237-
/// Returns desired capabilities for given [browser], [headless] and
238-
/// [chromeBinary].
243+
/// Returns desired capabilities for given [browser], [headless], [chromeBinary]
244+
/// and [webBrowserFlags].
239245
@visibleForTesting
240-
Map<String, dynamic> getDesiredCapabilities(Browser browser, bool? headless, [String? chromeBinary]) {
246+
Map<String, dynamic> getDesiredCapabilities(
247+
Browser browser,
248+
bool? headless, {
249+
List<String> webBrowserFlags = const <String>[],
250+
String? chromeBinary,
251+
}) {
241252
switch (browser) {
242253
case Browser.chrome:
243254
return <String, dynamic>{
@@ -262,6 +273,7 @@ Map<String, dynamic> getDesiredCapabilities(Browser browser, bool? headless, [St
262273
'--no-sandbox',
263274
'--no-first-run',
264275
if (headless!) '--headless',
276+
...webBrowserFlags,
265277
],
266278
'perfLoggingPrefs': <String, String>{
267279
'traceCategories':
@@ -278,6 +290,7 @@ Map<String, dynamic> getDesiredCapabilities(Browser browser, bool? headless, [St
278290
'moz:firefoxOptions' : <String, dynamic>{
279291
'args': <String>[
280292
if (headless!) '-headless',
293+
...webBrowserFlags,
281294
],
282295
'prefs': <String, dynamic>{
283296
'dom.file.createInChild': true,
@@ -313,7 +326,10 @@ Map<String, dynamic> getDesiredCapabilities(Browser browser, bool? headless, [St
313326
'platformName': 'android',
314327
'goog:chromeOptions': <String, dynamic>{
315328
'androidPackage': 'com.android.chrome',
316-
'args': <String>['--disable-fullscreen'],
329+
'args': <String>[
330+
'--disable-fullscreen',
331+
...webBrowserFlags,
332+
],
317333
},
318334
};
319335
}

packages/flutter_tools/lib/src/runner/flutter_command.dart

+10
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ class FlutterOptions {
119119
static const String kAssumeInitializeFromDillUpToDate = 'assume-initialize-from-dill-up-to-date';
120120
static const String kFatalWarnings = 'fatal-warnings';
121121
static const String kUseApplicationBinary = 'use-application-binary';
122+
static const String kWebBrowserFlag = 'web-browser-flag';
122123
}
123124

124125
/// flutter command categories for usage.
@@ -270,6 +271,15 @@ abstract class FlutterCommand extends Command<void> {
270271
help: 'The URL to provide to the browser. Defaults to an HTTP URL with the host '
271272
'name of "--web-hostname", the port of "--web-port", and the path set to "/".',
272273
);
274+
argParser.addMultiOption(
275+
FlutterOptions.kWebBrowserFlag,
276+
help: 'Additional flag to pass to a browser instance at startup.\n'
277+
'Chrome: https://www.chromium.org/developers/how-tos/run-chromium-with-flags/\n'
278+
'Firefox: https://wiki.mozilla.org/Firefox/CommandLineOptions\n'
279+
'Multiple flags can be passed by repeating "--${FlutterOptions.kWebBrowserFlag}" multiple times.',
280+
valueHelp: '--foo=bar',
281+
hide: !verboseHelp,
282+
);
273283
}
274284

275285
void usesTargetOption() {

packages/flutter_tools/lib/src/test/flutter_web_platform.dart

+8-1
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,8 @@ class BrowserManager {
659659
///
660660
/// The browser will start in headless mode if [headless] is true.
661661
///
662+
/// Add arbitrary browser flags via [webBrowserFlags].
663+
///
662664
/// The [settings] indicate how to invoke this browser's executable.
663665
///
664666
/// Returns the browser manager, or throws an [ApplicationException] if a
@@ -670,8 +672,13 @@ class BrowserManager {
670672
Future<WebSocketChannel> future, {
671673
bool debug = false,
672674
bool headless = true,
675+
List<String> webBrowserFlags = const <String>[],
673676
}) async {
674-
final Chromium chrome = await chromiumLauncher.launch(url.toString(), headless: headless);
677+
final Chromium chrome = await chromiumLauncher.launch(
678+
url.toString(),
679+
headless: headless,
680+
webBrowserFlags: webBrowserFlags,
681+
);
675682
final Completer<BrowserManager> completer = Completer<BrowserManager>();
676683

677684
unawaited(chrome.onExit.then((int? browserExitCode) {

packages/flutter_tools/lib/src/web/chrome.dart

+4
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,14 @@ class ChromiumLauncher {
164164
/// port is picked automatically.
165165
///
166166
/// [skipCheck] does not attempt to make a devtools connection before returning.
167+
///
168+
/// [webBrowserFlags] add arbitrary browser flags.
167169
Future<Chromium> launch(String url, {
168170
bool headless = false,
169171
int? debugPort,
170172
bool skipCheck = false,
171173
Directory? cacheDir,
174+
List<String> webBrowserFlags = const <String>[],
172175
}) async {
173176
if (currentCompleter.isCompleted) {
174177
throwToolExit('Only one instance of chrome can be started.');
@@ -215,6 +218,7 @@ class ChromiumLauncher {
215218
'--no-sandbox',
216219
'--window-size=2400,1800',
217220
],
221+
...webBrowserFlags,
218222
url,
219223
];
220224

packages/flutter_tools/lib/src/web/web_device.dart

+1
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ abstract class ChromiumDevice extends Device {
149149
.childDirectory('chrome-device'),
150150
headless: debuggingOptions.webRunHeadless,
151151
debugPort: debuggingOptions.webBrowserDebugPort,
152+
webBrowserFlags: debuggingOptions.webBrowserFlags,
152153
);
153154
}
154155
_logger.sendEvent('app.webLaunchUrl', <String, Object>{'url': url, 'launched': launchChrome});

packages/flutter_tools/test/commands.shard/hermetic/drive_test.dart

+1
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,7 @@ class FailingFakeDriverService extends Fake implements DriverService {
354354
String browserName,
355355
bool androidEmulator,
356356
int driverPort,
357+
List<String> webBrowserFlags,
357358
List<String> browserDimension,
358359
String profileMemory,
359360
}) async => 1;

0 commit comments

Comments
 (0)