From 7d0a194cfa76970c4cc43e4f42f2df3009cc266a Mon Sep 17 00:00:00 2001 From: Daniel Chevalier Date: Tue, 31 Oct 2023 16:55:05 -0400 Subject: [PATCH] New VSCode fallback behaviour for copyToClipboard (#6598) ![](https://media.giphy.com/media/4URfklUToxk6A/giphy.gif) Adds a fallback behaviour for copying which will allow copy buttons to actually work in VSCode. NOTE: success toasts will no longer show in VSCode though. I've also added some logging just in case a user is looking at their console when a copy fails. Hopefully this provides enough information that the unhide-able error doesn't look suspicious. ![Screenshot 2023-10-26 at 10 53 39 AM](https://github.com/flutter/devtools/assets/1386322/b4ebd139-f3f5-4dd6-8c62-794f7d6df80d) Related to https://github.com/Dart-Code/Dart-Code/pull/4814 Fixes https://github.com/flutter/devtools/issues/5775 Mirror package:web version from Flutter SDK --- .../lib/src/framework/settings_dialog.dart | 2 +- .../src/screens/network/network_screen.dart | 1 + .../lib/src/shared/common_widgets.dart | 1 + .../_copy_to_clipboard_desktop.dart | 7 +++ .../_copy_to_clipboard_web.dart | 19 +++++++ .../copy_to_clipboard/copy_to_clipboard.dart | 52 +++++++++++++++++++ .../devtools_app/lib/src/shared/dialogs.dart | 2 +- .../lib/src/shared/editable_list.dart | 2 +- .../devtools_app/lib/src/shared/utils.dart | 19 ------- packages/devtools_app/pubspec.yaml | 2 +- 10 files changed, 84 insertions(+), 23 deletions(-) create mode 100644 packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_desktop.dart create mode 100644 packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_web.dart create mode 100644 packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart diff --git a/packages/devtools_app/lib/src/framework/settings_dialog.dart b/packages/devtools_app/lib/src/framework/settings_dialog.dart index 3705255a32b..0e850ef7082 100644 --- a/packages/devtools_app/lib/src/framework/settings_dialog.dart +++ b/packages/devtools_app/lib/src/framework/settings_dialog.dart @@ -11,10 +11,10 @@ import 'package:provider/provider.dart'; import '../shared/analytics/analytics_controller.dart'; import '../shared/analytics/constants.dart' as gac; import '../shared/common_widgets.dart'; +import '../shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart'; import '../shared/config_specific/server/server.dart'; import '../shared/globals.dart'; import '../shared/log_storage.dart'; -import '../shared/utils.dart'; class OpenSettingsAction extends ScaffoldAction { OpenSettingsAction({super.key, Color? color}) diff --git a/packages/devtools_app/lib/src/screens/network/network_screen.dart b/packages/devtools_app/lib/src/screens/network/network_screen.dart index f84ff588f6f..d72610d5c49 100644 --- a/packages/devtools_app/lib/src/screens/network/network_screen.dart +++ b/packages/devtools_app/lib/src/screens/network/network_screen.dart @@ -13,6 +13,7 @@ import 'package:provider/provider.dart'; import '../../shared/analytics/analytics.dart' as ga; import '../../shared/analytics/constants.dart' as gac; import '../../shared/common_widgets.dart'; +import '../../shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart'; import '../../shared/globals.dart'; import '../../shared/http/curl_command.dart'; import '../../shared/http/http_request_data.dart'; diff --git a/packages/devtools_app/lib/src/shared/common_widgets.dart b/packages/devtools_app/lib/src/shared/common_widgets.dart index 3345b93de79..7a130f18b9c 100644 --- a/packages/devtools_app/lib/src/shared/common_widgets.dart +++ b/packages/devtools_app/lib/src/shared/common_widgets.dart @@ -17,6 +17,7 @@ import '../screens/debugger/debugger_controller.dart'; import '../screens/inspector/layout_explorer/ui/theme.dart'; import 'analytics/analytics.dart' as ga; import 'analytics/constants.dart' as gac; +import 'config_specific/copy_to_clipboard/copy_to_clipboard.dart'; import 'config_specific/launch_url/launch_url.dart'; import 'console/widgets/expandable_variable.dart'; import 'diagnostics/dart_object_node.dart'; diff --git a/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_desktop.dart b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_desktop.dart new file mode 100644 index 00000000000..04bef782f20 --- /dev/null +++ b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_desktop.dart @@ -0,0 +1,7 @@ +// Copyright 2023 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. + +void copyToClipboardVSCode(String _) { + // Do nothing +} diff --git a/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_web.dart b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_web.dart new file mode 100644 index 00000000000..e00b987428e --- /dev/null +++ b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/_copy_to_clipboard_web.dart @@ -0,0 +1,19 @@ +// Copyright 2023 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:js_interop'; +import 'package:web/web.dart'; + +void copyToClipboardVSCode(String data) { + // This post message is only relevant in VSCode. If the parent frame is + // listening for this command, then it will attempt to copy the contents + // to the clipboard in the context of the parent frame. + window.parent?.postMessage( + { + 'command': 'clipboard-write', + 'data': data, + }.jsify(), + '*'.toJS, + ); +} diff --git a/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart new file mode 100644 index 00000000000..455414e7898 --- /dev/null +++ b/packages/devtools_app/lib/src/shared/config_specific/copy_to_clipboard/copy_to_clipboard.dart @@ -0,0 +1,52 @@ +// Copyright 2023 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 'package:devtools_app_shared/ui.dart'; +import 'package:flutter/services.dart'; +import 'package:logging/logging.dart'; + +import '../../globals.dart'; +import '_copy_to_clipboard_desktop.dart' + if (dart.library.js_interop) '_copy_to_clipboard_web.dart'; + +final _log = Logger('copy_to_clipboard'); + +/// Attempts to copy a String of `data` to the clipboard. +/// +/// Shows a `successMessage` [Notification] on the passed in `context`, if the +/// copy is successfully done using the [Clipboard.setData] api. Otherwise it +/// attempts to post the [data] to the parent frame where the parent frame will +/// try to complete the copy (this fallback will only work in VSCode). +Future copyToClipboard( + String data, + String? successMessage, +) async { + try { + await Clipboard.setData( + ClipboardData( + text: data, + ), + ); + + if (successMessage != null) notificationService.push(successMessage); + } catch (e) { + if (ideTheme.embed) { + _log.warning( + 'DevTools copy failed. This may be as a result of a known bug in VSCode. ' + 'See https://github.com/Dart-Code/Dart-Code/issues/4540 for more ' + 'information. DevTools will now attempt to use a fallback method of ' + 'copying the contents.', + ); + // Trying to use Clipboard.setData to copy in vscode will not work as a + // result of a bug. So we should fallback to `copyToClipboardVSCode` which + // delegates the copy to the frame above DevTools. + // Sending this message in other environments will just have no effect. + // See https://github.com/Dart-Code/Dart-Code/issues/4540 for more + // information. + copyToClipboardVSCode(data); + } else { + rethrow; + } + } +} diff --git a/packages/devtools_app/lib/src/shared/dialogs.dart b/packages/devtools_app/lib/src/shared/dialogs.dart index 6b4abd22e66..f510379aabf 100644 --- a/packages/devtools_app/lib/src/shared/dialogs.dart +++ b/packages/devtools_app/lib/src/shared/dialogs.dart @@ -8,8 +8,8 @@ import 'package:devtools_app_shared/ui.dart'; import 'package:flutter/material.dart'; import '../shared/config_specific/launch_url/launch_url.dart'; +import 'config_specific/copy_to_clipboard/copy_to_clipboard.dart'; import 'globals.dart'; -import 'utils.dart'; /// A dialog, that reports unexpected error and allows to copy details and create issue. class UnexpectedErrorDialog extends StatelessWidget { diff --git a/packages/devtools_app/lib/src/shared/editable_list.dart b/packages/devtools_app/lib/src/shared/editable_list.dart index c6c08ba4a40..6d013120a67 100644 --- a/packages/devtools_app/lib/src/shared/editable_list.dart +++ b/packages/devtools_app/lib/src/shared/editable_list.dart @@ -10,7 +10,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'common_widgets.dart'; -import 'utils.dart'; +import 'config_specific/copy_to_clipboard/copy_to_clipboard.dart'; /// A widget that displays the contents of [entries]. /// diff --git a/packages/devtools_app/lib/src/shared/utils.dart b/packages/devtools_app/lib/src/shared/utils.dart index 3c691b9c361..29cd8d3d73b 100644 --- a/packages/devtools_app/lib/src/shared/utils.dart +++ b/packages/devtools_app/lib/src/shared/utils.dart @@ -6,13 +6,10 @@ // other libraries in this package. // Utils, that do not have dependencies, should go to primitives/utils.dart. -import 'dart:async'; - import 'package:devtools_app_shared/service.dart'; import 'package:devtools_app_shared/ui.dart'; import 'package:devtools_app_shared/utils.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:logging/logging.dart'; import 'package:provider/provider.dart'; @@ -25,22 +22,6 @@ import 'globals.dart'; final _log = Logger('lib/src/shared/utils'); -/// Attempts to copy a String of `data` to the clipboard. -/// -/// Shows a `successMessage` [Notification] on the passed in `context`. -Future copyToClipboard( - String data, - String? successMessage, -) async { - await Clipboard.setData( - ClipboardData( - text: data, - ), - ); - - if (successMessage != null) notificationService.push(successMessage); -} - /// Logging to debug console only in debug runs. void debugLogger(String message) { assert( diff --git a/packages/devtools_app/pubspec.yaml b/packages/devtools_app/pubspec.yaml index a9a62524e75..4d052f059f3 100644 --- a/packages/devtools_app/pubspec.yaml +++ b/packages/devtools_app/pubspec.yaml @@ -66,7 +66,7 @@ dependencies: vm_service: ^11.10.0 # TODO https://github.com/dart-lang/sdk/issues/52853 - unpin this version vm_snapshot_analysis: 0.7.2 - web: ^0.3.0 + web: ^0.2.1-beta web_socket_channel: ^2.1.0 # widget_icons: ^0.0.1