[google_maps_flutter_web] Fix InfoWindows and getLatLng. (#3163)

* Update package:google_maps to ^3.4.5.
* Fix GoogleMapController.getLatLng(): https://github.com/flutter/flutter/issues/67606
* Make InfoWindow contents clickable so onTap works as advertised: https://github.com/flutter/flutter/issues/67289
* Fix InfoWindow snippets when converting initial markers: https://github.com/flutter/flutter/issues/67854
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md
index 1d40d67..e4918ab 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md
+++ b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md
@@ -1,3 +1,10 @@
+## 0.1.0+5
+
+* Update `package:google_maps` to `^3.4.5`.
+* Fix `GoogleMapController.getLatLng()`. [Issue](https://github.com/flutter/flutter/issues/67606).
+* Make `InfoWindow` contents clickable so `onTap` works as advertised. [Issue](https://github.com/flutter/flutter/issues/67289).
+* Fix `InfoWindow` snippets when converting initial markers. [Issue](https://github.com/flutter/flutter/issues/67854).
+
 ## 0.1.0+4
 
 * Update `package:sanitize_html` to `^1.4.1` to prevent [a crash](https://github.com/flutter/flutter/issues/67854) when InfoWindow title/snippet have links.
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart
index 3e1b1f1..e8847fd 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart
@@ -270,14 +270,15 @@
         if (rawMarker['position'] != null) {
           position = LatLng.fromJson(rawMarker['position']);
         }
-        if (rawMarker['infoWindow'] != null || rawMarker['snippet'] != null) {
-          String title = rawMarker['infoWindow'] != null
-              ? rawMarker['infoWindow']['title']
-              : null;
-          infoWindow = InfoWindow(
-            title: title ?? '',
-            snippet: rawMarker['snippet'] ?? '',
-          );
+        if (rawMarker['infoWindow'] != null) {
+          final String title = rawMarker['infoWindow']['title'];
+          final String snippet = rawMarker['infoWindow']['snippet'];
+          if (title != null || snippet != null) {
+            infoWindow = InfoWindow(
+              title: title ?? '',
+              snippet: snippet ?? '',
+            );
+          }
         }
         return Marker(
           markerId: MarkerId(rawMarker['markerId']),
@@ -378,13 +379,28 @@
     return null;
   }
 
-  final content = '<h3 class="infowindow-title">' +
-      sanitizeHtml(marker.infoWindow.title ?? "") +
-      '</h3>' +
-      sanitizeHtml(marker.infoWindow.snippet ?? "");
+  // Add an outer wrapper to the contents of the infowindow, we need it to listen
+  // to click events...
+  final HtmlElement container = DivElement()
+    ..id = 'gmaps-marker-${marker.markerId.value}-infowindow';
+  if (marker.infoWindow.title?.isNotEmpty ?? false) {
+    final HtmlElement title = HeadingElement.h3()
+      ..className = 'infowindow-title'
+      ..innerText = marker.infoWindow.title;
+    container.children.add(title);
+  }
+  if (marker.infoWindow.snippet?.isNotEmpty ?? false) {
+    final HtmlElement snippet = DivElement()
+      ..className = 'infowindow-snippet'
+      ..setInnerHtml(
+        sanitizeHtml(marker.infoWindow.snippet),
+        treeSanitizer: NodeTreeSanitizer.trusted,
+      );
+    container.children.add(snippet);
+  }
 
   return gmaps.InfoWindowOptions()
-    ..content = content
+    ..content = container
     ..zIndex = marker.zIndex;
   // TODO: Compute the pixelOffset of the infoWindow, from the size of the Marker,
   // and the marker.infoWindow.anchor property.
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart
index 707af82..8195473 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart
@@ -256,9 +256,8 @@
 
   /// Returns the [LatLng] for a `screenCoordinate` (in pixels) of the viewport.
   Future<LatLng> getLatLng(ScreenCoordinate screenCoordinate) async {
-    final latLng = _googleMap.projection.fromPointToLatLng(
-      gmaps.Point(screenCoordinate.x, screenCoordinate.y),
-    );
+    final gmaps.LatLng latLng =
+        _pixelToLatLng(_googleMap, screenCoordinate.x, screenCoordinate.y);
     return _gmLatLngToLatLng(latLng);
   }
 
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart
index a067e35..30db477 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart
@@ -46,6 +46,10 @@
   /// Returns the [gmaps.Marker] associated to this controller.
   gmaps.Marker get marker => _marker;
 
+  /// Returns the [gmaps.InfoWindow] associated to the marker.
+  @visibleForTesting
+  gmaps.InfoWindow get infoWindow => _infoWindow;
+
   /// Updates the options of the wrapped [gmaps.Marker] object.
   void update(
     gmaps.MarkerOptions options, {
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart
index ebb478d..a0428ad 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart
@@ -38,10 +38,14 @@
     gmaps.InfoWindow gmInfoWindow;
 
     if (infoWindowOptions != null) {
-      gmInfoWindow = gmaps.InfoWindow(infoWindowOptions)
-        ..addListener('click', () {
+      gmInfoWindow = gmaps.InfoWindow(infoWindowOptions);
+      // Google Maps' JS SDK does not have a click event on the InfoWindow, so
+      // we make one...
+      if (infoWindowOptions.content is HtmlElement) {
+        infoWindowOptions.content.onClick.listen((_) {
           _onInfoWindowTap(marker.markerId);
         });
+      }
     }
 
     final currentMarker = _markerIdToController[marker.markerId]?.marker;
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml
index 6d0c917..a44d5c1 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml
+++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml
@@ -1,7 +1,7 @@
 name: google_maps_flutter_web
 description: Web platform implementation of google_maps_flutter
 homepage: https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter
-version: 0.1.0+4
+version: 0.1.0+5
 
 flutter:
   plugin:
@@ -17,7 +17,7 @@
     sdk: flutter
   meta: ^1.1.7
   google_maps_flutter_platform_interface: ^1.0.4
-  google_maps: ^3.0.0
+  google_maps: ^3.4.5
   stream_transform: ^1.2.0
   sanitize_html: ^1.4.1
 
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_integration.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_integration.dart
index 625ecb3..dfd01c6 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_integration.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/google_maps_controller_integration.dart
@@ -146,7 +146,13 @@
             {'circleId': 'circle-1'}
           ],
           'markersToAdd': [
-            {'markerId': 'marker-1'}
+            {
+              'markerId': 'marker-1',
+              'infoWindow': {
+                'title': 'title for test',
+                'snippet': 'snippet for test',
+              },
+            },
           ],
           'polygonsToAdd': [
             {
@@ -191,10 +197,34 @@
 
         expect(capturedCircles.first.circleId.value, 'circle-1');
         expect(capturedMarkers.first.markerId.value, 'marker-1');
+        expect(capturedMarkers.first.infoWindow.snippet, 'snippet for test');
+        expect(capturedMarkers.first.infoWindow.title, 'title for test');
         expect(capturedPolygons.first.polygonId.value, 'polygon-1');
         expect(capturedPolylines.first.polylineId.value, 'polyline-1');
       });
 
+      testWidgets('empty infoWindow does not create InfoWindow instance.',
+          (WidgetTester tester) async {
+        controller = _createController(options: {
+          'markersToAdd': [
+            {
+              'markerId': 'marker-1',
+              'infoWindow': {},
+            },
+          ],
+        });
+        controller.debugSetOverrides(
+          markers: markers,
+        );
+
+        controller.init();
+
+        final capturedMarkers =
+            verify(markers.addMarkers(captureAny)).captured[0] as Set<Marker>;
+
+        expect(capturedMarkers.first.infoWindow, isNull);
+      });
+
       group('Initialization options', () {
         gmaps.MapOptions capturedOptions;
         setUp(() {
@@ -390,6 +420,8 @@
       group('map.projection methods', () {
         // These are too much for dart mockito, can't mock:
         // map.projection.method() (in Javascript ;) )
+
+        // Caused https://github.com/flutter/flutter/issues/67606
       });
     });
 
diff --git a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_integration.dart b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_integration.dart
index 75e1af7..a813ff8 100644
--- a/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_integration.dart
+++ b/packages/google_maps_flutter/google_maps_flutter_web/test/test_driver/markers_integration.dart
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 import 'dart:async';
+import 'dart:html';
 
 import 'package:integration_test/integration_test.dart';
 import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
@@ -127,8 +128,39 @@
       controller.addMarkers(markers);
 
       expect(controller.markers.length, 1);
-      expect(controller.markers[MarkerId('1')].marker.title,
-          equals('title for test'));
+      final content =
+          controller.markers[MarkerId('1')].infoWindow.content as HtmlElement;
+      expect(content.innerHtml, contains('title for test'));
+      expect(
+          content.innerHtml,
+          contains(
+              '<a href="https://www.google.com">Go to Google &gt;&gt;&gt;</a>'));
+    });
+
+    // https://github.com/flutter/flutter/issues/67289
+    testWidgets('InfoWindow content is clickable', (WidgetTester tester) async {
+      final markers = {
+        Marker(
+          markerId: MarkerId('1'),
+          infoWindow: InfoWindow(
+            title: 'title for test',
+            snippet: 'some snippet',
+          ),
+        ),
+      };
+
+      controller.addMarkers(markers);
+
+      expect(controller.markers.length, 1);
+      final content =
+          controller.markers[MarkerId('1')].infoWindow.content as HtmlElement;
+
+      content.click();
+
+      final event = await stream.stream.first;
+
+      expect(event, isA<InfoWindowTapEvent>());
+      expect((event as InfoWindowTapEvent).value, equals(MarkerId('1')));
     });
   });
 }