web_device.dart 6.83 KB
Newer Older
1 2 3 4
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
import 'package:meta/meta.dart';

7 8 9
import '../application_package.dart';
import '../base/file_system.dart';
import '../base/io.dart';
10
import '../base/platform.dart';
11
import '../base/process_manager.dart';
12 13
import '../build_info.dart';
import '../device.dart';
14
import '../features.dart';
15
import '../globals.dart';
16
import '../project.dart';
17
import 'chrome.dart';
18
import 'workflow.dart';
19 20

class WebApplicationPackage extends ApplicationPackage {
21
  WebApplicationPackage(this.flutterProject) : super(id: flutterProject.manifest.appName);
22

23
  final FlutterProject flutterProject;
24 25

  @override
26
  String get name => flutterProject.manifest.appName;
27 28

  /// The location of the web source assets.
29
  Directory get webSourcePath => flutterProject.directory.childDirectory('web');
30 31
}

32 33 34
class ChromeDevice extends Device {
  ChromeDevice() : super(
      'chrome',
35 36 37 38
      category: Category.web,
      platformType: PlatformType.web,
      ephemeral: false,
  );
39

40 41 42
  /// The active chrome instance.
  Chrome _chrome;

43 44
  // TODO(jonahwilliams): this is technically false, but requires some refactoring
  // to allow hot mode restart only devices.
45
  @override
46
  bool get supportsHotReload => true;
47 48

  @override
49
  bool get supportsHotRestart => true;
50 51 52 53 54

  @override
  bool get supportsStartPaused => true;

  @override
55
  bool get supportsFlutterExit => true;
56 57 58 59 60

  @override
  bool get supportsScreenshot => false;

  @override
61
  void clearLogs() { }
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79

  @override
  DeviceLogReader getLogReader({ApplicationPackage app}) {
    return NoOpDeviceLogReader(app.name);
  }

  @override
  Future<bool> installApp(ApplicationPackage app) async => true;

  @override
  Future<bool> isAppInstalled(ApplicationPackage app) async => true;

  @override
  Future<bool> isLatestBuildInstalled(ApplicationPackage app) async => true;

  @override
  Future<bool> get isLocalEmulator async => false;

80 81 82
  @override
  Future<String> get emulatorId async => null;

83
  @override
84
  bool isSupported() =>  featureFlags.isWebEnabled && canFindChrome();
85 86

  @override
87
  String get name => 'Chrome';
88 89 90 91 92

  @override
  DevicePortForwarder get portForwarder => const NoOpDevicePortForwarder();

  @override
93
  Future<String> get sdkNameAndVersion async {
94 95 96
    if (!isSupported()) {
      return 'unknown';
    }
97 98 99 100
    // See https://bugs.chromium.org/p/chromium/issues/detail?id=158372
    String version = 'unknown';
    if (platform.isWindows) {
      final ProcessResult result = await processManager.run(<String>[
101
        r'reg', 'query', 'HKEY_CURRENT_USER\\Software\\Google\\Chrome\\BLBeacon', '/v', 'version',
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
      ]);
      if (result.exitCode == 0) {
        final List<String> parts = result.stdout.split(RegExp(r'\s+'));
        if (parts.length > 2) {
          version = 'Google Chrome ' + parts[parts.length - 2];
        }
      }
    } else {
      final String chrome = findChromeExecutable();
      final ProcessResult result = await processManager.run(<String>[
        chrome,
        '--version',
      ]);
      if (result.exitCode == 0) {
        version = result.stdout;
      }
118
    }
119
    return version.trim();
120
  }
121 122 123 124 125 126 127 128 129 130 131

  @override
  Future<LaunchResult> startApp(
    covariant WebApplicationPackage package, {
    String mainPath,
    String route,
    DebuggingOptions debuggingOptions,
    Map<String, Object> platformArgs,
    bool prebuiltApplication = false,
    bool ipv6 = false,
  }) async {
132 133
    // See [ResidentWebRunner.run] in flutter_tools/lib/src/resident_web_runner.dart
    // for the web initialization and server logic.
134 135 136 137 138 139 140
    final String url = platformArgs['uri'];
    if (debuggingOptions.browserLaunch) {
      _chrome = await chromeLauncher.launch(url);
    } else {
      printStatus('Waiting for connection from Dart debug extension at $url', emphasis: true);
      logger.sendNotification(url, progressId: 'debugExtension');
    }
141 142 143 144 145
    return LaunchResult.succeeded(observatoryUri: null);
  }

  @override
  Future<bool> stopApp(ApplicationPackage app) async {
146
    await _chrome?.close();
147 148 149 150
    return true;
  }

  @override
151
  Future<TargetPlatform> get targetPlatform async => TargetPlatform.web_javascript;
152 153 154 155

  @override
  Future<bool> uninstallApp(ApplicationPackage app) async => true;

156 157 158 159
  @override
  bool isSupportedForProject(FlutterProject flutterProject) {
    return flutterProject.web.existsSync();
  }
160 161 162
}

class WebDevices extends PollingDeviceDiscovery {
163
  WebDevices() : super('chrome');
164

165
  final ChromeDevice _webDevice = ChromeDevice();
166
  final WebServerDevice _webServerDevice = WebServerDevice();
167 168

  @override
169
  bool get canListAnything => featureFlags.isWebEnabled;
170 171 172 173 174

  @override
  Future<List<Device>> pollingGetDevices() async {
    return <Device>[
      _webDevice,
175
      _webServerDevice,
176 177 178 179
    ];
  }

  @override
180
  bool get supportsPlatform =>  featureFlags.isWebEnabled;
181
}
182 183 184 185 186

@visibleForTesting
String parseVersionForWindows(String input) {
  return input.split(RegExp('\w')).last;
}
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248


/// A special device type to allow serving for arbitrary browsers.
class WebServerDevice extends Device {
  WebServerDevice() : super(
    'web',
    platformType: PlatformType.web,
    category: Category.web,
    ephemeral: false,
  );

  @override
  void clearLogs() { }

  @override
  Future<String> get emulatorId => null;

  @override
  DeviceLogReader getLogReader({ApplicationPackage app}) {
    return NoOpDeviceLogReader(app.name);
  }

  @override
  Future<bool> installApp(ApplicationPackage app) async => true;

  @override
  Future<bool> isAppInstalled(ApplicationPackage app) async => true;

  @override
  Future<bool> isLatestBuildInstalled(ApplicationPackage app) async => true;

  @override
  Future<bool> get isLocalEmulator async => false;

  @override
  bool isSupported() => featureFlags.isWebEnabled;

  @override
  bool isSupportedForProject(FlutterProject flutterProject) {
    return flutterProject.web.existsSync();
  }

  @override
  String get name => 'Server';

  @override
  DevicePortForwarder get portForwarder => const NoOpDevicePortForwarder();

  @override
  Future<String> get sdkNameAndVersion async => 'Flutter Tools';

  @override
  Future<LaunchResult> startApp(ApplicationPackage package, {
    String mainPath,
    String route,
    DebuggingOptions debuggingOptions,
    Map<String, Object> platformArgs,
    bool prebuiltApplication = false,
    bool ipv6 = false,
  }) async {
    final String url = platformArgs['uri'];
    printStatus('$mainPath is being served at $url', emphasis: true);
249
    logger.sendNotification(url, progressId: 'debugExtension');
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
    return LaunchResult.succeeded(observatoryUri: null);
  }

  @override
  Future<bool> stopApp(ApplicationPackage app) async {
    return true;
  }

  @override
  Future<TargetPlatform> get targetPlatform async => TargetPlatform.web_javascript;

  @override
  Future<bool> uninstallApp(ApplicationPackage app) async {
    return true;
  }
}