diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index 7eb2107505aaa..818c35dbeadd7 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -247,6 +247,7 @@ executable("flutter_windows_unittests") { "keyboard_key_embedder_handler_unittests.cc", "keyboard_key_handler_unittests.cc", "keyboard_unittests.cc", + "platform_handler_unittests.cc", "testing/flutter_window_win32_test.cc", "testing/flutter_window_win32_test.h", "testing/mock_window_binding_handler.cc", diff --git a/shell/platform/windows/platform_handler.cc b/shell/platform/windows/platform_handler.cc index 85254e5653a62..3b6d87aebf002 100644 --- a/shell/platform/windows/platform_handler.cc +++ b/shell/platform/windows/platform_handler.cc @@ -9,6 +9,7 @@ static constexpr char kChannelName[] = "flutter/platform"; static constexpr char kGetClipboardDataMethod[] = "Clipboard.getData"; +static constexpr char kHasStringsClipboardMethod[] = "Clipboard.hasStrings"; static constexpr char kSetClipboardDataMethod[] = "Clipboard.setData"; static constexpr char kTextPlainFormat[] = "text/plain"; @@ -46,6 +47,15 @@ void PlatformHandler::HandleMethodCall( return; } GetPlainText(std::move(result), kTextKey); + } else if (method.compare(kHasStringsClipboardMethod) == 0) { + // Only one string argument is expected. + const rapidjson::Value& format = method_call.arguments()[0]; + + if (strcmp(format.GetString(), kTextPlainFormat) != 0) { + result->Error(kClipboardError, kUnknownClipboardFormatMessage); + return; + } + GetHasStrings(std::move(result)); } else if (method.compare(kSetClipboardDataMethod) == 0) { const rapidjson::Value& document = *method_call.arguments(); rapidjson::Value::ConstMemberIterator itr = document.FindMember(kTextKey); diff --git a/shell/platform/windows/platform_handler.h b/shell/platform/windows/platform_handler.h index dc16e8b779921..a4ceaa837abdf 100644 --- a/shell/platform/windows/platform_handler.h +++ b/shell/platform/windows/platform_handler.h @@ -31,6 +31,11 @@ class PlatformHandler { std::unique_ptr> result, std::string_view key) = 0; + // Provides a boolean to |result| as the value in a dictionary at key + // "value" representing whether or not the clipboard has a non-empty string. + virtual void GetHasStrings( + std::unique_ptr> result) = 0; + // Sets the clipboard's plain text to |text|, and reports the result (either // an error, or null for success) to |result|. virtual void SetPlainText( diff --git a/shell/platform/windows/platform_handler_unittests.cc b/shell/platform/windows/platform_handler_unittests.cc index c427ac080c7d2..6340cb6aa0a62 100644 --- a/shell/platform/windows/platform_handler_unittests.cc +++ b/shell/platform/windows/platform_handler_unittests.cc @@ -6,7 +6,6 @@ #include -#include "flutter/shell/platform/common/client_wrapper/include/flutter/binary_messenger.h" #include "flutter/shell/platform/common/json_method_codec.h" #include "flutter/shell/platform/windows/testing/test_binary_messenger.h" #include "gmock/gmock.h" @@ -22,9 +21,11 @@ using ::testing::_; static constexpr char kChannelName[] = "flutter/platform"; static constexpr char kGetClipboardDataMethod[] = "Clipboard.getData"; +static constexpr char kHasStringsClipboardMethod[] = "Clipboard.hasStrings"; static constexpr char kSetClipboardDataMethod[] = "Clipboard.setData"; static constexpr char kTextPlainFormat[] = "text/plain"; +static constexpr char kFakeContentType[] = "text/madeupcontenttype"; // Test implementation of PlatformHandler to allow testing the PlatformHandler // logic. @@ -38,7 +39,9 @@ class TestPlatformHandler : public PlatformHandler { // |PlatformHandler| MOCK_METHOD2(GetPlainText, void(std::unique_ptr>, - const char*)); + std::string_view key)); + MOCK_METHOD1(GetHasStrings, + void(std::unique_ptr>)); MOCK_METHOD2(SetPlainText, void(const std::string&, std::unique_ptr>)); @@ -61,9 +64,9 @@ TEST(PlatformHandler, GettingTextCallsThrough) { TestBinaryMessenger messenger; TestPlatformHandler platform_handler(&messenger); - auto args = std::make_unique(rapidjson::kArrayType); + auto args = std::make_unique(rapidjson::kStringType); auto& allocator = args->GetAllocator(); - args->PushBack(kTextPlainFormat, allocator); + args->SetString(kTextPlainFormat); auto encoded = JsonMethodCodec::GetInstance().EncodeMethodCall( MethodCall(kGetClipboardDataMethod, std::move(args))); @@ -85,9 +88,9 @@ TEST(PlatformHandler, RejectsGettingUnknownTypes) { TestBinaryMessenger messenger; TestPlatformHandler platform_handler(&messenger); - auto args = std::make_unique(rapidjson::kArrayType); + auto args = std::make_unique(rapidjson::kStringType); auto& allocator = args->GetAllocator(); - args->PushBack("madeup/contenttype", allocator); + args->SetString(kFakeContentType); auto encoded = JsonMethodCodec::GetInstance().EncodeMethodCall( MethodCall(kGetClipboardDataMethod, std::move(args))); @@ -103,6 +106,53 @@ TEST(PlatformHandler, RejectsGettingUnknownTypes) { })); } +TEST(PlatformHandler, GetHasStringsCallsThrough) { + TestBinaryMessenger messenger; + TestPlatformHandler platform_handler(&messenger); + + auto args = std::make_unique(rapidjson::kStringType); + auto& allocator = args->GetAllocator(); + args->SetString(kTextPlainFormat); + auto encoded = JsonMethodCodec::GetInstance().EncodeMethodCall( + MethodCall(kHasStringsClipboardMethod, + std::move(args))); + + // Set up a handler to call a response on |result| so that it doesn't log + // on destruction about leaking. + ON_CALL(platform_handler, GetHasStrings) + .WillByDefault( + [](std::unique_ptr> result) { + result->NotImplemented(); + }); + + EXPECT_CALL(platform_handler, GetHasStrings(_)); + EXPECT_TRUE(messenger.SimulateEngineMessage( + kChannelName, encoded->data(), encoded->size(), + [](const uint8_t* reply, size_t reply_size) {})); +} + +TEST(PlatformHandler, RejectsGetHasStringsOnUnknownTypes) { + TestBinaryMessenger messenger; + TestPlatformHandler platform_handler(&messenger); + + auto args = std::make_unique(rapidjson::kStringType); + auto& allocator = args->GetAllocator(); + args->SetString(kFakeContentType); + auto encoded = JsonMethodCodec::GetInstance().EncodeMethodCall( + MethodCall(kHasStringsClipboardMethod, + std::move(args))); + + MockMethodResult result; + // Requsting an unknow content type is an error. + EXPECT_CALL(result, ErrorInternal(_, _, _)); + EXPECT_TRUE(messenger.SimulateEngineMessage( + kChannelName, encoded->data(), encoded->size(), + [&](const uint8_t* reply, size_t reply_size) { + JsonMethodCodec::GetInstance().DecodeAndProcessResponseEnvelope( + reply, reply_size, &result); + })); +} + TEST(PlatformHandler, SettingTextCallsThrough) { TestBinaryMessenger messenger; TestPlatformHandler platform_handler(&messenger); diff --git a/shell/platform/windows/platform_handler_win32.cc b/shell/platform/windows/platform_handler_win32.cc index b4f0ea351601e..1985f85ed27b5 100644 --- a/shell/platform/windows/platform_handler_win32.cc +++ b/shell/platform/windows/platform_handler_win32.cc @@ -13,6 +13,8 @@ #include "flutter/shell/platform/windows/flutter_windows_view.h" #include "flutter/shell/platform/windows/string_conversion.h" +static constexpr char kValueKey[] = "value"; + namespace flutter { namespace { @@ -229,6 +231,24 @@ void PlatformHandlerWin32::GetPlainText( result->Success(document); } +void PlatformHandlerWin32::GetHasStrings( + std::unique_ptr> result) { + ScopedClipboard clipboard; + if (!clipboard.Open(std::get(*view_->GetRenderTarget()))) { + rapidjson::Document error_code; + error_code.SetInt(::GetLastError()); + result->Error(kClipboardError, "Unable to open clipboard", error_code); + return; + } + + rapidjson::Document document; + document.SetObject(); + rapidjson::Document::AllocatorType& allocator = document.GetAllocator(); + document.AddMember(rapidjson::Value(kValueKey, allocator), + rapidjson::Value(clipboard.HasString()), allocator); + result->Success(document); +} + void PlatformHandlerWin32::SetPlainText( const std::string& text, std::unique_ptr> result) { diff --git a/shell/platform/windows/platform_handler_win32.h b/shell/platform/windows/platform_handler_win32.h index 2005f75b84ee5..8477830f12b4e 100644 --- a/shell/platform/windows/platform_handler_win32.h +++ b/shell/platform/windows/platform_handler_win32.h @@ -28,6 +28,10 @@ class PlatformHandlerWin32 : public PlatformHandler { void GetPlainText(std::unique_ptr> result, std::string_view key) override; + // |PlatformHandler| + void GetHasStrings( + std::unique_ptr> result) override; + // |PlatformHandler| void SetPlainText( const std::string& text, diff --git a/shell/platform/windows/platform_handler_winuwp.cc b/shell/platform/windows/platform_handler_winuwp.cc index e984d1fc9fd79..e3ea7e5b9566a 100644 --- a/shell/platform/windows/platform_handler_winuwp.cc +++ b/shell/platform/windows/platform_handler_winuwp.cc @@ -28,6 +28,12 @@ void PlatformHandlerWinUwp::GetPlainText( result->NotImplemented(); } +void PlatformHandlerWinUwp::GetHasStrings( + std::unique_ptr> result) { + // TODO: Implement. See https://github.com/flutter/flutter/issues/70214. + result->NotImplemented(); +} + void PlatformHandlerWinUwp::SetPlainText( const std::string& text, std::unique_ptr> result) { diff --git a/shell/platform/windows/platform_handler_winuwp.h b/shell/platform/windows/platform_handler_winuwp.h index e067988c1415d..cae4f5ae65fab 100644 --- a/shell/platform/windows/platform_handler_winuwp.h +++ b/shell/platform/windows/platform_handler_winuwp.h @@ -28,6 +28,10 @@ class PlatformHandlerWinUwp : public PlatformHandler { void GetPlainText(std::unique_ptr> result, std::string_view key) override; + // |PlatformHandler| + void GetHasStrings( + std::unique_ptr> result) override; + // |PlatformHandler| void SetPlainText( const std::string& text,