Merge branch 'master' into path-endorse
diff --git a/.cirrus.yml b/.cirrus.yml
index 1236002..753bc90 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -6,6 +6,8 @@
     dockerfile: .ci/Dockerfile
     cpu: 8
     memory: 16G
+  env:
+    E2E_PATH: "./packages/e2e"
   upgrade_script:
     - flutter channel stable
     - flutter upgrade
@@ -45,6 +47,19 @@
         - if [[ "$CHANNEL" -eq "stable" ]]; then find . | grep _web$ | xargs rm -rf; fi
         - flutter channel $CHANNEL
         - ./script/build_all_plugins_app.sh apk
+    - name: e2e_web_smoke_test
+      # Tests e2e example test in web.
+      only_if: "changesInclude('.cirrus.yml', 'packages/e2e/**') || $CIRRUS_PR == ''"
+      install_script:
+        - flutter config --enable-web
+        - git clone https://github.com/flutter/web_installers.git
+        - cd web_installers/packages/web_drivers/
+        - pub get
+        - dart lib/web_driver_installer.dart chromedriver --install-only
+        - ./chromedriver/chromedriver --port=4444 &
+      test_script:
+        - cd $E2E_PATH/example/
+        - flutter drive -v --target=test_driver/example_e2e.dart -d web-server --release --browser-name=chrome
     - name: build-apks+java-test+firebase-test-lab
       env:
         matrix:
diff --git a/packages/e2e/CHANGELOG.md b/packages/e2e/CHANGELOG.md
index 35d3159..86f37b2 100644
--- a/packages/e2e/CHANGELOG.md
+++ b/packages/e2e/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.2.4+4
+
+* Fixed a hang that occurred on platforms that don't have a `MethodChannel` listener registered..
+
 ## 0.2.4+3
 
 * Fixed code snippet in the readme under the "Using Flutter driver to run tests" section.
diff --git a/packages/e2e/README.md b/packages/e2e/README.md
index ed892ab..3e27ff3 100644
--- a/packages/e2e/README.md
+++ b/packages/e2e/README.md
@@ -69,6 +69,15 @@
 flutter drive --driver=test_driver/<package_name>_test.dart test/<package_name>_e2e.dart
 ```
 
+You can run tests on web on release mode.
+
+First you need to make sure you have downloaded the driver for the browser.
+
+```
+cd example
+flutter drive -v --target=test_driver/<package_name>dart -d web-server --release --browser-name=chrome
+```
+
 ## Android device testing
 
 Create an instrumentation test file in your application's
diff --git a/packages/e2e/example/lib/main.dart b/packages/e2e/example/lib/main.dart
index 2509fb3..1f33324 100644
--- a/packages/e2e/example/lib/main.dart
+++ b/packages/e2e/example/lib/main.dart
@@ -1,27 +1,5 @@
-import 'dart:io' show Platform;
-import 'package:flutter/material.dart';
+import 'my_app.dart' if (dart.library.html) 'my_web_app.dart';
 
 // ignore_for_file: public_member_api_docs
 
-void main() => runApp(MyApp());
-
-class MyApp extends StatefulWidget {
-  @override
-  _MyAppState createState() => _MyAppState();
-}
-
-class _MyAppState extends State<MyApp> {
-  @override
-  Widget build(BuildContext context) {
-    return MaterialApp(
-      home: Scaffold(
-        appBar: AppBar(
-          title: const Text('Plugin example app'),
-        ),
-        body: Center(
-          child: Text('Platform: ${Platform.operatingSystem}\n'),
-        ),
-      ),
-    );
-  }
-}
+void main() => startApp();
diff --git a/packages/e2e/example/lib/my_app.dart b/packages/e2e/example/lib/my_app.dart
new file mode 100644
index 0000000..bfbdb86
--- /dev/null
+++ b/packages/e2e/example/lib/my_app.dart
@@ -0,0 +1,27 @@
+import 'dart:io' show Platform;
+import 'package:flutter/material.dart';
+
+// ignore_for_file: public_member_api_docs
+
+void startApp() => runApp(MyApp());
+
+class MyApp extends StatefulWidget {
+  @override
+  _MyAppState createState() => _MyAppState();
+}
+
+class _MyAppState extends State<MyApp> {
+  @override
+  Widget build(BuildContext context) {
+    return MaterialApp(
+      home: Scaffold(
+        appBar: AppBar(
+          title: const Text('Plugin example app'),
+        ),
+        body: Center(
+          child: Text('Platform: ${Platform.operatingSystem}\n'),
+        ),
+      ),
+    );
+  }
+}
diff --git a/packages/e2e/example/lib/my_web_app.dart b/packages/e2e/example/lib/my_web_app.dart
new file mode 100644
index 0000000..c2ced1a
--- /dev/null
+++ b/packages/e2e/example/lib/my_web_app.dart
@@ -0,0 +1,28 @@
+import 'dart:html' as html;
+import 'package:flutter/material.dart';
+
+// ignore_for_file: public_member_api_docs
+
+void startApp() => runApp(MyWebApp());
+
+class MyWebApp extends StatefulWidget {
+  @override
+  _MyWebAppState createState() => _MyWebAppState();
+}
+
+class _MyWebAppState extends State<MyWebApp> {
+  @override
+  Widget build(BuildContext context) {
+    return MaterialApp(
+      home: Scaffold(
+        appBar: AppBar(
+          title: const Text('Plugin example app'),
+        ),
+        body: Center(
+          key: Key('mainapp'),
+          child: Text('Platform: ${html.window.navigator.platform}\n'),
+        ),
+      ),
+    );
+  }
+}
diff --git a/packages/e2e/example/test_driver/example_e2e.dart b/packages/e2e/example/test_driver/example_e2e.dart
index e91dd4d..d97702d 100644
--- a/packages/e2e/example/test_driver/example_e2e.dart
+++ b/packages/e2e/example/test_driver/example_e2e.dart
@@ -5,27 +5,12 @@
 // gestures. You can also use WidgetTester to find child widgets in the widget
 // tree, read text, and verify that the values of widget properties are correct.
 
-import 'dart:io' show Platform;
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
 import 'package:e2e/e2e.dart';
 
-import 'package:e2e_example/main.dart';
+import 'example_e2e_io.dart' if (dart.library.html) 'example_e2e_web.dart'
+    as tests;
 
 void main() {
   E2EWidgetsFlutterBinding.ensureInitialized();
-  testWidgets('verify text', (WidgetTester tester) async {
-    // Build our app and trigger a frame.
-    await tester.pumpWidget(MyApp());
-
-    // Verify that platform version is retrieved.
-    expect(
-      find.byWidgetPredicate(
-        (Widget widget) =>
-            widget is Text &&
-            widget.data.startsWith('Platform: ${Platform.operatingSystem}'),
-      ),
-      findsOneWidget,
-    );
-  });
+  tests.main();
 }
diff --git a/packages/e2e/example/test_driver/example_e2e_io.dart b/packages/e2e/example/test_driver/example_e2e_io.dart
new file mode 100644
index 0000000..9766f56
--- /dev/null
+++ b/packages/e2e/example/test_driver/example_e2e_io.dart
@@ -0,0 +1,34 @@
+// This is a basic Flutter widget test.
+//
+// To perform an interaction with a widget in your test, use the WidgetTester
+// utility that Flutter provides. For example, you can send tap and scroll
+// gestures. You can also use WidgetTester to find child widgets in the widget
+// tree, read text, and verify that the values of widget properties are correct.
+
+import 'dart:io' show Platform;
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:e2e/e2e.dart';
+
+import 'package:e2e_example/main.dart' as app;
+
+void main() {
+  E2EWidgetsFlutterBinding.ensureInitialized();
+  testWidgets('verify text', (WidgetTester tester) async {
+    // Build our app and trigger a frame.
+    app.main();
+
+    // Trigger a frame.
+    await tester.pumpAndSettle();
+
+    // Verify that platform version is retrieved.
+    expect(
+      find.byWidgetPredicate(
+        (Widget widget) =>
+            widget is Text &&
+            widget.data.startsWith('Platform: ${Platform.operatingSystem}'),
+      ),
+      findsOneWidget,
+    );
+  });
+}
diff --git a/packages/e2e/example/test_driver/example_e2e_web.dart b/packages/e2e/example/test_driver/example_e2e_web.dart
new file mode 100644
index 0000000..24c3f2c
--- /dev/null
+++ b/packages/e2e/example/test_driver/example_e2e_web.dart
@@ -0,0 +1,35 @@
+// This is a basic Flutter widget test.
+//
+// To perform an interaction with a widget in your test, use the WidgetTester
+// utility that Flutter provides. For example, you can send tap and scroll
+// gestures. You can also use WidgetTester to find child widgets in the widget
+// tree, read text, and verify that the values of widget properties are correct.
+
+import 'dart:html' as html;
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:e2e/e2e.dart';
+
+import 'package:e2e_example/main.dart' as app;
+
+void main() {
+  E2EWidgetsFlutterBinding.ensureInitialized();
+  testWidgets('verify text', (WidgetTester tester) async {
+    // Build our app and trigger a frame.
+    app.main();
+
+    // Trigger a frame.
+    await tester.pumpAndSettle();
+
+    // Verify that platform is retrieved.
+    expect(
+      find.byWidgetPredicate(
+        (Widget widget) =>
+            widget is Text &&
+            widget.data
+                .startsWith('Platform: ${html.window.navigator.platform}\n'),
+      ),
+      findsOneWidget,
+    );
+  });
+}
diff --git a/packages/e2e/example/test_driver/failure.dart b/packages/e2e/example/test_driver/failure.dart
index 64d83bf..ddeeb80 100644
--- a/packages/e2e/example/test_driver/failure.dart
+++ b/packages/e2e/example/test_driver/failure.dart
@@ -6,7 +6,7 @@
 import 'package:flutter_test/flutter_test.dart';
 import 'package:e2e/e2e.dart';
 
-import 'package:e2e_example/main.dart';
+import 'package:e2e_example/main.dart' as app;
 
 // Tests the failure behavior of the E2EWidgetsFlutterBinding
 //
@@ -21,10 +21,10 @@
 
   testWidgets('failure 1', (WidgetTester tester) async {
     // Build our app and trigger a frame.
-    await tester.pumpWidget(MyApp());
+    app.main();
 
     // Verify that platform version is retrieved.
-    expect(
+    await expectLater(
       find.byWidgetPredicate(
         (Widget widget) =>
             widget is Text && widget.data.startsWith('This should fail'),
diff --git a/packages/e2e/example/web/favicon.png b/packages/e2e/example/web/favicon.png
new file mode 100644
index 0000000..8aaa46a
--- /dev/null
+++ b/packages/e2e/example/web/favicon.png
Binary files differ
diff --git a/packages/e2e/example/web/icons/Icon-192.png b/packages/e2e/example/web/icons/Icon-192.png
new file mode 100644
index 0000000..b749bfe
--- /dev/null
+++ b/packages/e2e/example/web/icons/Icon-192.png
Binary files differ
diff --git a/packages/e2e/example/web/icons/Icon-512.png b/packages/e2e/example/web/icons/Icon-512.png
new file mode 100644
index 0000000..88cfd48
--- /dev/null
+++ b/packages/e2e/example/web/icons/Icon-512.png
Binary files differ
diff --git a/packages/e2e/example/web/index.html b/packages/e2e/example/web/index.html
new file mode 100644
index 0000000..9662965
--- /dev/null
+++ b/packages/e2e/example/web/index.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="UTF-8">
+  <meta content="IE=Edge" http-equiv="X-UA-Compatible">
+  <meta name="description" content="A new Flutter project.">
+
+  <!-- iOS meta tags & icons -->
+  <meta name="apple-mobile-web-app-capable" content="yes">
+  <meta name="apple-mobile-web-app-status-bar-style" content="black">
+  <meta name="apple-mobile-web-app-title" content="example">
+  <link rel="apple-touch-icon" href="/icons/Icon-192.png">
+
+  <!-- Favicon -->
+  <link rel="shortcut icon" type="image/png" href="/favicon.png"/>
+
+  <title>example</title>
+  <link rel="manifest" href="/manifest.json">
+</head>
+<body>
+  <!-- This script installs service_worker.js to provide PWA functionality to
+       application. For more information, see:
+       https://developers.google.com/web/fundamentals/primers/service-workers -->
+  <script>
+    if ('serviceWorker' in navigator) {
+      window.addEventListener('load', function () {
+        navigator.serviceWorker.register('/flutter_service_worker.js');
+      });
+    }
+  </script>
+  <script src="main.dart.js" type="application/javascript"></script>
+</body>
+</html>
diff --git a/packages/e2e/example/web/manifest.json b/packages/e2e/example/web/manifest.json
new file mode 100644
index 0000000..c638001
--- /dev/null
+++ b/packages/e2e/example/web/manifest.json
@@ -0,0 +1,23 @@
+{
+    "name": "example",
+    "short_name": "example",
+    "start_url": ".",
+    "display": "minimal-ui",
+    "background_color": "#0175C2",
+    "theme_color": "#0175C2",
+    "description": "A new Flutter project.",
+    "orientation": "portrait-primary",
+    "prefer_related_applications": false,
+    "icons": [
+        {
+            "src": "icons/Icon-192.png",
+            "sizes": "192x192",
+            "type": "image/png"
+        },
+        {
+            "src": "icons/Icon-512.png",
+            "sizes": "512x512",
+            "type": "image/png"
+        }
+    ]
+}
diff --git a/packages/e2e/lib/e2e.dart b/packages/e2e/lib/e2e.dart
index afcbeb0..eef594a 100644
--- a/packages/e2e/lib/e2e.dart
+++ b/packages/e2e/lib/e2e.dart
@@ -19,6 +19,12 @@
     // TODO(jackson): Report test results as they arrive
     tearDownAll(() async {
       try {
+        // For web integration tests we are not using the
+        // `plugins.flutter.io/e2e`. Mark the tests as complete before invoking
+        // the channel.
+        if (kIsWeb) {
+          if (!_allTestsPassed.isCompleted) _allTestsPassed.complete(true);
+        }
         await _channel.invokeMethod<void>(
             'allTestsFinished', <String, dynamic>{'results': _results});
       } on MissingPluginException {
diff --git a/packages/e2e/pubspec.yaml b/packages/e2e/pubspec.yaml
index 6873586..21e1210 100644
--- a/packages/e2e/pubspec.yaml
+++ b/packages/e2e/pubspec.yaml
@@ -1,6 +1,6 @@
 name: e2e
 description: Runs tests that use the flutter_test API as integration tests.
-version: 0.2.4+3
+version: 0.2.4+4
 homepage: https://github.com/flutter/plugins/tree/master/packages/e2e
 
 environment:
diff --git a/packages/path_provider/path_provider/CHANGELOG.md b/packages/path_provider/path_provider/CHANGELOG.md
index 050d59a..622d28d 100644
--- a/packages/path_provider/path_provider/CHANGELOG.md
+++ b/packages/path_provider/path_provider/CHANGELOG.md
@@ -2,7 +2,6 @@
 
 * Endorsed macOS implementation.
 
-
 ## 1.6.3
 
 * Use `path_provider_platform_interface` in core plugin.
diff --git a/packages/path_provider/path_provider/pubspec.yaml b/packages/path_provider/path_provider/pubspec.yaml
index c99d2f2..8b40207 100644
--- a/packages/path_provider/path_provider/pubspec.yaml
+++ b/packages/path_provider/path_provider/pubspec.yaml
@@ -2,7 +2,11 @@
 description: Flutter plugin for getting commonly used locations on the Android &
   iOS file systems, such as the temp and app data directories.
 homepage: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider
+<<<<<<< HEAD
 version: 1.6.4
+=======
+version: 1.6.3
+>>>>>>> master
 
 flutter:
   plugin:
@@ -20,10 +24,14 @@
 dependencies:
   flutter:
     sdk: flutter
+<<<<<<< HEAD
   platform: ^2.0.0
   meta: ^1.0.5
   path_provider_platform_interface: ^1.0.1
   path_provider_macos: ^0.0.4
+=======
+  path_provider_platform_interface: ^1.0.1
+>>>>>>> master
 
 dev_dependencies:
   e2e: ^0.2.1
diff --git a/packages/path_provider/path_provider/test/path_provider_test.dart b/packages/path_provider/path_provider/test/path_provider_test.dart
index 4e23442..d3511f5 100644
--- a/packages/path_provider/path_provider/test/path_provider_test.dart
+++ b/packages/path_provider/path_provider/test/path_provider_test.dart
@@ -11,7 +11,11 @@
 import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
 import 'package:plugin_platform_interface/plugin_platform_interface.dart';
 
+<<<<<<< HEAD
 const String kTemproraryPath = 'temporaryPath';
+=======
+const String kTemporaryPath = 'temporaryPath';
+>>>>>>> master
 const String kApplicationSupportPath = 'applicationSupportPath';
 const String kDownloadsPath = 'downloadsPath';
 const String kLibraryPath = 'libraryPath';
@@ -29,7 +33,11 @@
 
     test('getTemporaryDirectory', () async {
       Directory result = await getTemporaryDirectory();
+<<<<<<< HEAD
       expect(result.path, kTemproraryPath);
+=======
+      expect(result.path, kTemporaryPath);
+>>>>>>> master
     });
 
     test('getApplicationSupportDirectory', () async {
@@ -75,7 +83,11 @@
     with MockPlatformInterfaceMixin
     implements PathProviderPlatform {
   Future<String> getTemporaryPath() async {
+<<<<<<< HEAD
     return kTemproraryPath;
+=======
+    return kTemporaryPath;
+>>>>>>> master
   }
 
   Future<String> getApplicationSupportPath() async {