New VSCode fallback behaviour for copyToClipboard (#6598)  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.  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
diff --git a/packages/devtools_app/lib/src/framework/settings_dialog.dart b/packages/devtools_app/lib/src/framework/settings_dialog.dart index 3705255..0e850ef 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 '../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 f84ff58..d72610d 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 '../../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 3345b93..7a130f1 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/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 0000000..04bef78 --- /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 0000000..e00b987 --- /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 0000000..455414e --- /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<void> 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 6b4abd2..f510379 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: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 c6c08ba..6d01312 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/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 3c691b9..29cd8d3 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 @@ 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<void> 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 a9a6252..4d052f0 100644 --- a/packages/devtools_app/pubspec.yaml +++ b/packages/devtools_app/pubspec.yaml
@@ -66,7 +66,7 @@ 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