Skip to content

Commit aea660d

Browse files
authored
feat(tool): Respect user-data-dir flag from web-browser-flag (flutter#169445)
### Respect user-data-dir flag from web-browser-flag Currently, it's already possible to pass `web-browser-flag` when launching Chrome, but the `user-data-dir` flag doesn't work as expected, and there are some reasons for that. In the implementation made in [PR flutter#104935](flutter#104935), the `web-browser-flag` is appended at the end of the Chrome launch arguments. For most scenarios, this works fine, as demonstrated in the Chrome unit test below: https://source.chromium.org/chromium/chromium/src/+/main:base/command_line_unittest.cc ``` TEST(CommandLineTest, MultipleSameSwitch) { const CommandLine::CharType* argv[] = { FILE_PATH_LITERAL("program"), FILE_PATH_LITERAL("--foo=one"), // --foo first time FILE_PATH_LITERAL("-baz"), FILE_PATH_LITERAL("--foo=two") // --foo second time }; CommandLine cl(std::size(argv), argv); EXPECT_TRUE(cl.HasSwitch("foo")); EXPECT_TRUE(cl.HasSwitch("baz")); EXPECT_EQ("two", cl.GetSwitchValueASCII("foo")); } ``` In this scenario, the parser will consider the last occurrence of a flag. However, this behavior does not apply to certain flags, because Chrome processes some of them based on the first occurrence, not the last. This is the case for `--user-data-dir`, which is parsed very early during Chrome startup. The proposed code checks whether `--user-data-dir` was provided via `web-browser-flag`, and if so, it uses that value instead of the default `%temp%\flutter_tools_chrome_device.xpto` temporary directory. This also resolve this comment: flutter#104935 (comment) Example: `launch.json` ``` { "version": "0.2.0", "configurations": [ { "name": "flutter", "request": "launch", "type": "dart" }, { "name": "flutter (profile mode)", "request": "launch", "type": "dart", "flutterMode": "profile" }, { "name": "flutter (release mode)", "request": "launch", "type": "dart", "flutterMode": "release" }, { "name": "Flutter for web (hot reloadable)", "type": "dart", "request": "launch", "program": "lib/main.dart", "args": [ "-d", "chrome", "--web-port=5000", "--web-experimental-hot-reload", "--web-browser-flag=--user-data-dir=D:\\_Desenv\\flutter_tests\\chrome_profile" ] } ] } ``` `chrome://version` | Before | After | |--------|--------| | ![before](https://github.com/user-attachments/assets/b3f33419-fbf1-446e-a471-745df2e8c4f6) | ![after](https://github.com/user-attachments/assets/ea74a203-c269-4ce1-8a9b-f97e9a4cdc78) | Folder | Before | After | |--------|--------| | ![before_files](https://github.com/user-attachments/assets/8522e60b-d0eb-4b66-a4b6-c2d6ed34c15b) | ![after_files](https://github.com/user-attachments/assets/ff94b498-b319-474c-89e5-c55f6e1c4383) | ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
1 parent c1c38ad commit aea660d

File tree

2 files changed

+51
-3
lines changed

2 files changed

+51
-3
lines changed

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

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'dart:async';
66

7+
import 'package:collection/collection.dart';
78
import 'package:meta/meta.dart';
89
import 'package:process/process.dart';
910
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart' hide StackTrace;
@@ -145,6 +146,31 @@ class ChromiumLauncher {
145146
/// The executable this launcher will use.
146147
String findExecutable() => _browserFinder(_platform, _fileSystem);
147148

149+
/// Creates a user data directory for Chrome based on provided flags or creates a temporary one.
150+
///
151+
/// This method handles the creation of Chrome's user data directory in two ways:
152+
/// 1. If webBrowserFlags contains a --user-data-dir flag, it uses that directory
153+
/// 2. Otherwise, it creates a temporary directory in the system's temp location
154+
///
155+
/// The user data directory is where Chrome stores user preferences, cookies,
156+
/// and other session data. Using a temporary directory ensures a clean state
157+
/// for each launch, while allowing custom directories through flags for
158+
/// persistent configurations.
159+
Directory _createUserDataDirectory(List<String> webBrowserFlags) {
160+
if (webBrowserFlags.isNotEmpty) {
161+
final String? userDataDirFlag = webBrowserFlags.firstWhereOrNull(
162+
(String flag) => flag.startsWith('--user-data-dir='),
163+
);
164+
165+
if (userDataDirFlag != null) {
166+
final Directory userDataDir = _fileSystem.directory(userDataDirFlag.split('=')[1]);
167+
webBrowserFlags.remove(userDataDirFlag);
168+
return userDataDir;
169+
}
170+
}
171+
return _fileSystem.systemTempDirectory.createTempSync('flutter_tools_chrome_device.');
172+
}
173+
148174
/// Launch a Chromium browser to a particular `host` page.
149175
///
150176
/// [headless] defaults to false, and controls whether we open a headless or
@@ -189,9 +215,7 @@ class ChromiumLauncher {
189215
}
190216
}
191217

192-
final Directory userDataDir = _fileSystem.systemTempDirectory.createTempSync(
193-
'flutter_tools_chrome_device.',
194-
);
218+
final Directory userDataDir = _createUserDataDirectory(webBrowserFlags);
195219

196220
if (cacheDir != null) {
197221
// Seed data dir with previous state.

packages/flutter_tools/test/web.shard/chrome_test.dart

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -931,6 +931,30 @@ void main() {
931931
await chrome.close();
932932
},
933933
);
934+
935+
testWithoutContext('respects custom user data directory flag', () async {
936+
const String customUserDataDir = '/custom/chrome/data/dir';
937+
processManager.addCommand(
938+
const FakeCommand(
939+
command: <String>[
940+
'example_chrome',
941+
'--user-data-dir=$customUserDataDir',
942+
'--remote-debugging-port=12345',
943+
...kChromeArgs,
944+
'example_url',
945+
],
946+
stderr: kDevtoolsStderr,
947+
),
948+
);
949+
950+
await expectReturnsNormallyLater(
951+
chromeLauncher.launch(
952+
'example_url',
953+
skipCheck: true,
954+
webBrowserFlags: <String>['--user-data-dir=$customUserDataDir'],
955+
),
956+
);
957+
});
934958
}
935959

936960
/// Fake chrome connection that fails to get tabs a few times.

0 commit comments

Comments
 (0)