diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index b22a4abb21c1c..07534cf8a1b2b 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -388,6 +388,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/bitmap_canvas.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/browser_detection.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/browser_location.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvas_pool.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/clipboard.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/color_filter.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/canvas.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/canvas_kit_canvas.dart diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index f000505c5e953..bff7091bf2f4b 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -24,6 +24,7 @@ part 'engine/bitmap_canvas.dart'; part 'engine/browser_detection.dart'; part 'engine/browser_location.dart'; part 'engine/canvas_pool.dart'; +part 'engine/clipboard.dart'; part 'engine/color_filter.dart'; part 'engine/compositor/canvas.dart'; part 'engine/compositor/canvas_kit_canvas.dart'; diff --git a/lib/web_ui/lib/src/engine/clipboard.dart b/lib/web_ui/lib/src/engine/clipboard.dart new file mode 100644 index 0000000000000..d7c462cea7e0a --- /dev/null +++ b/lib/web_ui/lib/src/engine/clipboard.dart @@ -0,0 +1,109 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +part of engine; + +/// Handles clipboard related platform messages. +class ClipboardMessageHandler { + /// Helper to handle copy to clipboard functionality. + final CopyToClipboardStrategy _copyToClipboardStrategy = + CopyToClipboardStrategy(); + + /// Helper to handle copy to clipboard functionality. + final PasteFromClipboardStrategy _pasteFromClipboardStrategy = + PasteFromClipboardStrategy(); + + /// Handles the platform message which stores the given text to the clipboard. + void setDataMethodCall(MethodCall methodCall) { + _copyToClipboardStrategy.setData(methodCall.arguments['text']); + } + + /// Handles the platform message which retrieves text data from the clipboard. + void getDataMethodCall(ui.PlatformMessageResponseCallback callback) { + _pasteFromClipboardStrategy.getData().then((String data) { + const MethodCodec codec = JSONMethodCodec(); + final Map map = {'text': data}; + callback(codec.encodeSuccessEnvelope(map)); + }).catchError( + (error) => print('Could not get text from clipboard: $error')); + } +} + +/// Provides functionality for writing text to clipboard. +/// +/// A concrete implementation is picked at runtime based on the available +/// APIs and the browser. +abstract class CopyToClipboardStrategy { + factory CopyToClipboardStrategy() { + return (html.window.navigator.clipboard.writeText != null) + ? ClipboardAPICopyStrategy() + : ExecCommandCopyStrategy(); + } + + /// Places the text onto the browser Clipboard. + void setData(String text); +} + +/// Provides functionality for reading text from clipboard. +/// +/// A concrete implementation is picked at runtime based on the available +/// APIs and the browser. +abstract class PasteFromClipboardStrategy { + factory PasteFromClipboardStrategy() { + return (browserEngine == BrowserEngine.firefox || + html.window.navigator.clipboard.readText == null) + ? ExecCommandPasteStrategy() + : ClipboardAPIPasteStrategy(); + } + + /// Returns text from the system Clipboard. + Future getData(); +} + +/// Provides copy functionality for browsers which supports ClipboardAPI. +/// +/// Works on Chrome and Firefox browsers. +/// See: https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API +class ClipboardAPICopyStrategy implements CopyToClipboardStrategy { + @override + void setData(String text) { + html.window.navigator.clipboard + .writeText(text) + .catchError((error) => print('Could not copy text: $error')); + } +} + +/// Provides paste functionality for browsers which supports `clipboard.readText`. +/// +/// Works on Chrome. Firefox only supports `readText` if the target element is +/// in content editable mode. +/// See: https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Editable_content +/// See: https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API +class ClipboardAPIPasteStrategy implements PasteFromClipboardStrategy { + @override + Future getData() async { + return html.window.navigator.clipboard.readText(); + } +} + +/// Provides a fallback strategy for browsers which does not support ClipboardAPI. +class ExecCommandCopyStrategy implements CopyToClipboardStrategy { + @override + void setData(String text) { + // TODO(nurhan): https://github.com/flutter/flutter/issues/48578 + print('Clipboard is only implemented for browsers ' + 'supporting Clipboard API. Use context menu for text editing.'); + } +} + +/// Provides a fallback strategy for browsers which does not support ClipboardAPI. +class ExecCommandPasteStrategy implements PasteFromClipboardStrategy { + @override + Future getData() { + // TODO(nurhan): https://github.com/flutter/flutter/issues/48581 + // TODO(nurhan): https://github.com/flutter/flutter/issues/48580 + print('Paste is not implemented for this browser.'); + throw UnimplementedError(); + } +} diff --git a/lib/web_ui/lib/src/engine/window.dart b/lib/web_ui/lib/src/engine/window.dart index 5572247faf641..dd26fbe900def 100644 --- a/lib/web_ui/lib/src/engine/window.dart +++ b/lib/web_ui/lib/src/engine/window.dart @@ -159,10 +159,10 @@ class EngineWindow extends ui.Window { // There are no default system sounds on web. return; case 'Clipboard.setData': + ClipboardMessageHandler().setDataMethodCall(decoded); + return; case 'Clipboard.getData': - // TODO(nurhan): https://github.com/flutter/flutter/issues/46020 - print('WARNING: Clipboard API unimplemented for Flutter for Web. ' - 'Use context menu for text editing.'); + ClipboardMessageHandler().getDataMethodCall(callback); return; } break;