[ DWDS ] Write to WebkitDebugger.onClose if the debugger connection closes and the attempts to reconnect fail (#2785)

The Flutter tool expects DebugConnection.onDone to complete if the user exits the browser and the debugger connection closes.

After receiving notification of a closed connection, WebkitDebugger tries to reconnect to the debugger.  If these attempts fail, WebkitDebugger will now write to its onClose stream so that DebugConnection will know to signal onDone.

See https://github.com/flutter/flutter/issues/182897
diff --git a/dwds/CHANGELOG.md b/dwds/CHANGELOG.md
index 3da791e..8a7f8fe 100644
--- a/dwds/CHANGELOG.md
+++ b/dwds/CHANGELOG.md
@@ -12,6 +12,7 @@
 - Fix serialization of `HotRestartRequest` in `AppConnection`.
 - Split integration tests across DDC module systems.
 - Split additional tests across DDC module systems.
+- Fix issue where `DebugConnection` did not complete `onDone` if `WebkitDebugger` fails to reconnect to the debugger after the connection closes.
 
 ## 27.0.0
 - Remove `package:built_value`, `package:built_value_generator`, and `package:built_collection` dependencies.
diff --git a/dwds/lib/src/debugging/webkit_debugger.dart b/dwds/lib/src/debugging/webkit_debugger.dart
index c8a1954..8a59ffd 100644
--- a/dwds/lib/src/debugging/webkit_debugger.dart
+++ b/dwds/lib/src/debugging/webkit_debugger.dart
@@ -69,13 +69,7 @@
       await sub.cancel();
       if (_closed != null) {
         // The connection closing is expected.
-        _onClosedController.add(connection);
-        await Future.wait([
-          for (final controller in _controllers) controller.close(),
-          for (final MapEntry(value: (:controller, transformer: _))
-              in _eventStreams.entries)
-            controller.close(),
-        ]);
+        await _shutdown(connection);
         return;
       }
       var retry = false;
@@ -94,7 +88,11 @@
           retryCount++;
         }
       } while (retry && retryCount <= maxAttempts);
-      _initialize();
+      if (retryCount > maxAttempts) {
+        await _shutdown(connection);
+      } else {
+        _initialize();
+      }
     });
 
     final runtime = _wipDebugger.connection.runtime;
@@ -125,6 +123,16 @@
       ]);
   }
 
+  Future<void> _shutdown(WipConnection connection) async {
+    _onClosedController.add(connection);
+    await Future.wait([
+      for (final controller in _controllers) controller.close(),
+      for (final MapEntry(value: (:controller, transformer: _))
+          in _eventStreams.entries)
+        controller.close(),
+    ]);
+  }
+
   @override
   Stream<ConsoleAPIEvent> get onConsoleAPICalled =>
       _onConsoleAPICalledController.stream;