Channel buffers (#40165)

Started reading stored channel messages from ui.channelBuffers when handlers get set.
diff --git a/packages/flutter/lib/src/services/binding.dart b/packages/flutter/lib/src/services/binding.dart
index 2fb3d3b..428ebe1 100644
--- a/packages/flutter/lib/src/services/binding.dart
+++ b/packages/flutter/lib/src/services/binding.dart
@@ -186,8 +186,11 @@
     ByteData response;
     try {
       final MessageHandler handler = _handlers[channel];
-      if (handler != null)
+      if (handler != null) {
         response = await handler(data);
+      } else {
+        ui.channelBuffers.push(channel, data, callback);
+      }
     } catch (exception, stack) {
       FlutterError.reportError(FlutterErrorDetails(
         exception: exception,
@@ -214,6 +217,9 @@
       _handlers.remove(channel);
     else
       _handlers[channel] = handler;
+    ui.channelBuffers.drain(channel, (ByteData data, ui.PlatformMessageResponseCallback callback) async {
+      await handlePlatformMessage(channel, data, callback);
+    });
   }
 
   @override
diff --git a/packages/flutter/test/services/channel_buffers_test.dart b/packages/flutter/test/services/channel_buffers_test.dart
new file mode 100644
index 0000000..3c249d5
--- /dev/null
+++ b/packages/flutter/test/services/channel_buffers_test.dart
@@ -0,0 +1,50 @@
+// Copyright 2016 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.
+
+import 'dart:convert';
+import 'dart:typed_data';
+import 'dart:ui' as ui;
+import 'package:flutter/foundation.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+class TestChannelBuffersFlutterBinding extends BindingBase with ServicesBinding {
+}
+
+void main() {
+  ByteData _makeByteData(String str) {
+    final List<int> list = utf8.encode(str);
+    final ByteBuffer buffer = list is Uint8List ? list.buffer : Uint8List.fromList(list).buffer;
+    return ByteData.view(buffer);
+  }
+
+  String _getString(ByteData data) {
+    final ByteBuffer buffer = data.buffer;
+    final List<int> list = buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
+    return utf8.decode(list);
+  }
+
+  test('does drain channel buffers', () async {
+    const String channel = 'foo';
+    final TestChannelBuffersFlutterBinding binding = TestChannelBuffersFlutterBinding();
+    expect(binding.defaultBinaryMessenger, isNotNull);
+    bool didCallCallback = false;
+    final ui.PlatformMessageResponseCallback callback = (ByteData responseData) {
+      didCallCallback = true;
+    };
+    const String payload = 'bar';
+    final ByteData data = _makeByteData(payload);
+    ui.channelBuffers.push(channel, data, callback);
+    bool didDrainData = false;
+    binding.defaultBinaryMessenger.setMessageHandler(channel, (ByteData message) async {
+      expect(_getString(message), payload);
+      didDrainData= true;
+      return null;
+    });
+    // Flush the event queue.
+    await Future<void>((){});
+    expect(didDrainData, isTrue);
+    expect(didCallCallback, isTrue);
+  });
+}
diff --git a/packages/flutter_web_plugins/lib/src/plugin_registry.dart b/packages/flutter_web_plugins/lib/src/plugin_registry.dart
index a48d50c..a867b2a 100644
--- a/packages/flutter_web_plugins/lib/src/plugin_registry.dart
+++ b/packages/flutter_web_plugins/lib/src/plugin_registry.dart
@@ -80,6 +80,8 @@
       final MessageHandler handler = _handlers[channel];
       if (handler != null) {
         response = await handler(data);
+      } else {
+        ui.channelBuffers.push(channel, data, callback);
       }
     } catch (exception, stack) {
       FlutterError.reportError(FlutterErrorDetails(
@@ -119,6 +121,9 @@
       _handlers.remove(channel);
     else
       _handlers[channel] = handler;
+    ui.channelBuffers.drain(channel, (ByteData data, ui.PlatformMessageResponseCallback callback) async {
+      await handlePlatformMessage(channel, data, callback);
+    });
   }
 
   @override