From 39caf3500ef915d3fd67b6525e613d25af91dcd4 Mon Sep 17 00:00:00 2001 From: Ferhat Date: Tue, 27 Apr 2021 11:53:16 -0700 Subject: [PATCH] Fix incorrect platformchannel response when clipboard getData is not supported (flutter/engine#25743) --- .../lib/web_ui/lib/src/engine/clipboard.dart | 26 ++++++++++++++--- .../test/engine/platform_dispatcher_test.dart | 28 +++++++++++++++++++ 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/clipboard.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/clipboard.dart index 0ddd34190f7..71e42bb9476 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/clipboard.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/clipboard.dart @@ -45,12 +45,29 @@ class ClipboardMessageHandler { final Map map = {'text': data}; callback!(codec.encodeSuccessEnvelope(map)); }).catchError((dynamic error) { - print('Could not get text from clipboard: $error'); - callback!(codec.encodeErrorEnvelope( - code: 'paste_fail', message: 'Clipboard.getData failed')); + if (error is UnimplementedError) { + // Clipboard.getData not supported. + // Passing [null] to [callback] indicates that the platform message isn't + // implemented. Look at [MethodChannel.invokeMethod] to see how [null] is + // handled. + Future.delayed(Duration.zero).then((_) { + if (callback != null) { + callback(null); + } + }); + return; + } + _reportGetDataFailure(callback, codec, error); }); } + void _reportGetDataFailure(ui.PlatformMessageResponseCallback? callback, + MethodCodec codec, dynamic error) { + print('Could not get text from clipboard: $error'); + callback!(codec.encodeErrorEnvelope( + code: 'paste_fail', message: 'Clipboard.getData failed')); + } + /// Methods used by tests. set pasteFromClipboardStrategy(PasteFromClipboardStrategy strategy) { _pasteFromClipboardStrategy = strategy; @@ -185,6 +202,7 @@ class ExecCommandPasteStrategy implements PasteFromClipboardStrategy { @override Future getData() { // TODO(nurhan): https://github.com/flutter/flutter/issues/48581 - throw UnimplementedError('Paste is not implemented for this browser.'); + return Future.error( + UnimplementedError('Paste is not implemented for this browser.')); } } diff --git a/engine/src/flutter/lib/web_ui/test/engine/platform_dispatcher_test.dart b/engine/src/flutter/lib/web_ui/test/engine/platform_dispatcher_test.dart index 8bd86934c73..c3fb5c7d5f7 100644 --- a/engine/src/flutter/lib/web_ui/test/engine/platform_dispatcher_test.dart +++ b/engine/src/flutter/lib/web_ui/test/engine/platform_dispatcher_test.dart @@ -3,6 +3,8 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:html' as html; +import 'dart:js_util' as js_util; import 'dart:typed_data'; import 'package:ui/src/engine.dart'; @@ -55,5 +57,31 @@ void testMain() { true, ); }); + + test('responds correctly to flutter/platform Clipboard.getData failure', + () async { + // Patch browser so that clipboard api is not available. + dynamic originalClipboard = + js_util.getProperty(html.window.navigator, 'clipboard'); + js_util.setProperty(html.window.navigator, 'clipboard', null); + const MethodCodec codec = JSONMethodCodec(); + final Completer completer = Completer(); + ui.PlatformDispatcher.instance.sendPlatformMessage( + 'flutter/platform', + codec.encodeMethodCall(MethodCall( + 'Clipboard.getData', + )), + completer.complete, + ); + final ByteData? response = await completer.future; + if (response != null) { + expect( + () => codec.decodeEnvelope(response), + throwsA(isA()), + ); + } + js_util.setProperty( + html.window.navigator, 'clipboard', originalClipboard); + }); }); }