mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
copy chrome preferences to seeded data dir (#44032)
This commit is contained in:
parent
df0501c7d3
commit
3422540bc4
@ -4,6 +4,7 @@
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
|
||||
|
||||
import '../base/common.dart';
|
||||
@ -65,13 +66,18 @@ String findChromeExecutable() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
void resetChromeForTesting() {
|
||||
ChromeLauncher._currentCompleter = Completer<Chrome>();
|
||||
}
|
||||
|
||||
/// Responsible for launching chrome with devtools configured.
|
||||
class ChromeLauncher {
|
||||
const ChromeLauncher();
|
||||
|
||||
static bool get hasChromeInstance => _currentCompleter.isCompleted;
|
||||
|
||||
static final Completer<Chrome> _currentCompleter = Completer<Chrome>();
|
||||
static Completer<Chrome> _currentCompleter = Completer<Chrome>();
|
||||
|
||||
/// Whether we can locate the chrome executable.
|
||||
bool canFindChrome() {
|
||||
@ -89,15 +95,31 @@ class ChromeLauncher {
|
||||
/// a `headfull` browser.
|
||||
///
|
||||
/// `skipCheck` does not attempt to make a devtools connection before returning.
|
||||
Future<Chrome> launch(String url, { bool headless = false, bool skipCheck = false }) async {
|
||||
Future<Chrome> launch(String url, { bool headless = false, bool skipCheck = false, Directory dataDir }) async {
|
||||
// This is a JSON file which contains configuration from the
|
||||
// browser session, such as window position. It is located
|
||||
// under the Chrome data-dir folder.
|
||||
final String preferencesPath = fs.path.join('Default', 'preferences');
|
||||
|
||||
final String chromeExecutable = findChromeExecutable();
|
||||
final Directory dataDir = fs.systemTempDirectory.createTempSync('flutter_tool.');
|
||||
final Directory activeDataDir = fs.systemTempDirectory.createTempSync('flutter_tool.');
|
||||
// Seed data dir with previous state.
|
||||
|
||||
final File savedPreferencesFile = fs.file(fs.path.join(dataDir?.path ?? '', preferencesPath));
|
||||
final File destinationFile = fs.file(fs.path.join(activeDataDir.path, preferencesPath));
|
||||
if (dataDir != null) {
|
||||
if (savedPreferencesFile.existsSync()) {
|
||||
destinationFile.parent.createSync(recursive: true);
|
||||
savedPreferencesFile.copySync(destinationFile.path);
|
||||
}
|
||||
}
|
||||
|
||||
final int port = await os.findFreePort();
|
||||
final List<String> args = <String>[
|
||||
chromeExecutable,
|
||||
// Using a tmp directory ensures that a new instance of chrome launches
|
||||
// allowing for the remote debug port to be enabled.
|
||||
'--user-data-dir=${dataDir.path}',
|
||||
'--user-data-dir=${activeDataDir.path}',
|
||||
'--remote-debugging-port=$port',
|
||||
// When the DevTools has focus we don't want to slow down the application.
|
||||
'--disable-background-timer-throttling',
|
||||
@ -117,6 +139,21 @@ class ChromeLauncher {
|
||||
|
||||
final Process process = await processManager.start(args);
|
||||
|
||||
// When the process exits, copy the user settings back to the provided
|
||||
// data-dir.
|
||||
if (dataDir != null) {
|
||||
unawaited(process.exitCode.whenComplete(() {
|
||||
if (destinationFile.existsSync()) {
|
||||
savedPreferencesFile.parent.createSync(recursive: true);
|
||||
// If the file contains a crash string, remove it to hide
|
||||
// the popup on next run.
|
||||
final String contents = destinationFile.readAsStringSync();
|
||||
savedPreferencesFile.writeAsStringSync(contents
|
||||
.replaceFirst('"exit_type":"Crashed"', '"exit_type":"Normal"'));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// Wait until the DevTools are listening before trying to connect.
|
||||
await process.stderr
|
||||
.transform(utf8.decoder)
|
||||
|
@ -132,7 +132,10 @@ class ChromeDevice extends Device {
|
||||
// for the web initialization and server logic.
|
||||
final String url = platformArgs['uri'];
|
||||
if (debuggingOptions.browserLaunch) {
|
||||
_chrome = await chromeLauncher.launch(url);
|
||||
_chrome = await chromeLauncher.launch(url,
|
||||
dataDir: fs.currentDirectory
|
||||
.childDirectory('.dart_tool')
|
||||
.childDirectory('chrome-device'));
|
||||
} else {
|
||||
printStatus('Waiting for connection from Dart debug extension at $url', emphasis: true);
|
||||
logger.sendNotification(url, progressId: 'debugExtension');
|
||||
|
@ -19,33 +19,43 @@ import '../../src/testbed.dart';
|
||||
|
||||
void main() {
|
||||
Testbed testbed;
|
||||
Completer<int> exitCompleter;
|
||||
|
||||
setUp(() {
|
||||
final MockPlatform platform = MockPlatform();
|
||||
exitCompleter = Completer<int>.sync();
|
||||
when(platform.isWindows).thenReturn(false);
|
||||
final MockFileSystem mockFileSystem = MockFileSystem();
|
||||
testbed = Testbed(overrides: <Type, Generator>{
|
||||
ProcessManager: () => MockProcessManager(),
|
||||
Platform: () => platform,
|
||||
OperatingSystemUtils: () => MockOperatingSystemUtils(),
|
||||
FileSystem: () => mockFileSystem,
|
||||
}, setup: () {
|
||||
when(os.findFreePort()).thenAnswer((Invocation invocation) async {
|
||||
return 1234;
|
||||
});
|
||||
when(platform.environment).thenReturn(<String, String>{
|
||||
kChromeEnvironment: 'example_chrome',
|
||||
});
|
||||
when(processManager.start(any))
|
||||
.thenAnswer((Invocation invocation) async {
|
||||
return FakeProcess(
|
||||
exitCode: exitCompleter.future,
|
||||
stdout: const Stream<List<int>>.empty(),
|
||||
stderr: Stream<List<int>>.fromIterable(<List<int>>[
|
||||
utf8.encode('\n\nDevTools listening\n\n'),
|
||||
]),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
resetChromeForTesting();
|
||||
});
|
||||
|
||||
test('can launch chrome and connect to the devtools', () => testbed.run(() async {
|
||||
when(os.findFreePort()).thenAnswer((Invocation invocation) async {
|
||||
return 1234;
|
||||
});
|
||||
when(platform.environment).thenReturn(<String, String>{
|
||||
kChromeEnvironment: 'example_chrome',
|
||||
});
|
||||
final Directory mockDirectory = MockDirectory();
|
||||
when(fs.systemTempDirectory).thenReturn(mockDirectory);
|
||||
when(mockDirectory.createTempSync(any)).thenReturn(mockDirectory);
|
||||
when(mockDirectory.path).thenReturn('example');
|
||||
when(processManager.start(<String>[
|
||||
const List<String> expected = <String>[
|
||||
'example_chrome',
|
||||
'--user-data-dir=example',
|
||||
'--remote-debugging-port=1234',
|
||||
'--disable-background-timer-throttling',
|
||||
'--disable-extensions',
|
||||
@ -56,22 +66,45 @@ void main() {
|
||||
'--disable-default-apps',
|
||||
'--disable-translate',
|
||||
'example_url',
|
||||
])).thenAnswer((Invocation invocation) async {
|
||||
return FakeProcess(
|
||||
exitCode: Completer<int>().future,
|
||||
stdout: const Stream<List<int>>.empty(),
|
||||
stderr: Stream<List<int>>.fromIterable(<List<int>>[
|
||||
utf8.encode('\n\nDevTools listening\n\n'),
|
||||
]),
|
||||
);
|
||||
});
|
||||
];
|
||||
|
||||
await chromeLauncher.launch('example_url', skipCheck: true);
|
||||
final VerificationResult result = verify(processManager.start(captureAny));
|
||||
|
||||
expect(result.captured.single, containsAll(expected));
|
||||
}));
|
||||
|
||||
test('can seed chrome temp directory with existing preferences', () => testbed.run(() async {
|
||||
final Directory dataDir = fs.directory('chrome-stuff');
|
||||
final File preferencesFile = dataDir
|
||||
.childDirectory('Default')
|
||||
.childFile('preferences');
|
||||
preferencesFile
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('example');
|
||||
|
||||
await chromeLauncher.launch('example_url', skipCheck: true, dataDir: dataDir);
|
||||
final VerificationResult result = verify(processManager.start(captureAny));
|
||||
final String arg = result.captured.single
|
||||
.firstWhere((String arg) => arg.startsWith('--user-data-dir='));
|
||||
final Directory tempDirectory = fs.directory(arg.split('=')[1]);
|
||||
final File tempFile = tempDirectory
|
||||
.childDirectory('Default')
|
||||
.childFile('preferences');
|
||||
|
||||
expect(tempFile.existsSync(), true);
|
||||
expect(tempFile.readAsStringSync(), 'example');
|
||||
|
||||
// write crash to file:
|
||||
tempFile.writeAsStringSync('"exit_type":"Crashed"');
|
||||
exitCompleter.complete(0);
|
||||
|
||||
// writes non-crash back to dart_tool
|
||||
expect(preferencesFile.readAsStringSync(), '"exit_type":"Normal"');
|
||||
}));
|
||||
}
|
||||
|
||||
class MockProcessManager extends Mock implements ProcessManager {}
|
||||
class MockPlatform extends Mock implements Platform {}
|
||||
class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {}
|
||||
class MockFileSystem extends Mock implements FileSystem {}
|
||||
class MockDirectory extends Mock implements Directory {}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user