blob: de76d354531075ff169a91340ebab579c5abce31 [file] [log] [blame]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:io';
import 'package:pedantic/pedantic.dart';
import 'package:test_api/src/backend/runtime.dart'; // ignore: implementation_imports
import 'package:test_core/src/util/io.dart'; // ignore: implementation_imports
import '../executable_settings.dart';
import 'browser.dart';
import 'default_settings.dart';
// TODO(nweiz): move this into its own package?
/// A class for running an instance of Chrome.
///
/// Most of the communication with the browser is expected to happen via HTTP,
/// so this exposes a bare-bones API. The browser starts as soon as the class is
/// constructed, and is killed when [close] is called.
///
/// Any errors starting or running the process are reported through [onExit].
class Chrome extends Browser {
@override
final name = 'Chrome';
@override
final Future<Uri> remoteDebuggerUrl;
/// Starts a new instance of Chrome open to the given [url], which may be a
/// [Uri] or a [String].
factory Chrome(Uri url, {ExecutableSettings settings, bool debug = false}) {
settings ??= defaultSettings[Runtime.chrome];
var remoteDebuggerCompleter = Completer<Uri>.sync();
return Chrome._(() async {
var tryPort = ([int port]) async {
var dir = createTempDir();
var args = [
'--user-data-dir=$dir',
url.toString(),
'--disable-extensions',
'--disable-popup-blocking',
'--bwsi',
'--no-first-run',
'--no-default-browser-check',
'--disable-default-apps',
'--disable-translate',
'--disable-dev-shm-usage',
];
if (!debug && settings.headless) {
args.addAll([
'--headless',
'--disable-gpu',
// We don't actually connect to the remote debugger, but Chrome will
// close as soon as the page is loaded if we don't turn it on.
'--remote-debugging-port=0'
]);
}
args.addAll(settings.arguments);
// Currently, Chrome doesn't provide any way of ensuring that this port
// was successfully bound. It produces an error if the binding fails,
// but without a reliable and fast way to tell if it succeeded that
// doesn't provide us much. It's very unlikely that this port will fail,
// though.
if (port != null) args.add('--remote-debugging-port=$port');
var process = await Process.start(settings.executable, args);
if (port != null) {
remoteDebuggerCompleter.complete(
getRemoteDebuggerUrl(Uri.parse('http://localhost:$port')));
} else {
remoteDebuggerCompleter.complete(null);
}
unawaited(process.exitCode
.then((_) => Directory(dir).deleteSync(recursive: true)));
return process;
};
if (!debug) return tryPort();
return getUnusedPort<Process>(tryPort);
}, remoteDebuggerCompleter.future);
}
Chrome._(Future<Process> Function() startBrowser, this.remoteDebuggerUrl)
: super(startBrowser);
}