6 #include <rapidjson/document.h>
10 #include "flutter/fml/macros.h"
14 #include "flutter/shell/platform/windows/testing/engine_modifier.h"
15 #include "flutter/shell/platform/windows/testing/flutter_windows_engine_builder.h"
16 #include "flutter/shell/platform/windows/testing/mock_window_binding_handler.h"
17 #include "flutter/shell/platform/windows/testing/test_binary_messenger.h"
18 #include "flutter/shell/platform/windows/testing/windows_test.h"
19 #include "gmock/gmock.h"
20 #include "gtest/gtest.h"
27 : text_input_plugin(text_input_plugin) {}
30 text_input_plugin->view_id_ = view_id;
42 using ::testing::Return;
44 static constexpr
char kScanCodeKey[] =
"scanCode";
45 static constexpr
int kHandledScanCode = 20;
46 static constexpr
int kUnhandledScanCode = 21;
48 static constexpr
int kDefaultClientId = 42;
50 static constexpr
char kChannelName[] =
"flutter/textinput";
52 static constexpr
char kViewId[] =
"viewId";
55 static constexpr
char kTextKey[] =
"text";
63 "TextInputClient.updateEditingState";
65 static std::unique_ptr<std::vector<uint8_t>> CreateResponse(
bool handled) {
67 std::make_unique<rapidjson::Document>(rapidjson::kObjectType);
68 auto& allocator = response_doc->GetAllocator();
69 response_doc->AddMember(
"handled", handled, allocator);
73 static std::unique_ptr<rapidjson::Document> EncodedClientConfig(
74 std::string type_name,
75 std::string input_action) {
76 auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
77 auto& allocator = arguments->GetAllocator();
78 arguments->PushBack(kDefaultClientId, allocator);
80 rapidjson::Value config(rapidjson::kObjectType);
81 config.AddMember(
"inputAction", input_action, allocator);
83 config.AddMember(
kViewId, 456, allocator);
84 rapidjson::Value type_info(rapidjson::kObjectType);
85 type_info.AddMember(
"name", type_name, allocator);
86 config.AddMember(
"inputType", type_info, allocator);
87 arguments->PushBack(config, allocator);
92 static std::unique_ptr<rapidjson::Document> EncodedEditingState(
94 TextRange selection) {
95 auto model = std::make_unique<TextInputModel>();
97 model->SetSelection(selection);
99 auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
100 auto& allocator = arguments->GetAllocator();
101 arguments->PushBack(kDefaultClientId, allocator);
103 rapidjson::Value editing_state(rapidjson::kObjectType);
111 model->composing() ? model->composing_range().base() : -1;
112 int composing_extent =
113 model->composing() ? model->composing_range().extent() : -1;
117 rapidjson::Value(model->GetText(), allocator).Move(),
119 arguments->PushBack(editing_state, allocator);
124 class MockFlutterWindowsView :
public FlutterWindowsView {
127 std::unique_ptr<WindowBindingHandler> window)
131 MOCK_METHOD(
void, OnCursorRectUpdated, (
const Rect&), (
override));
132 MOCK_METHOD(
void, OnResetImeComposing, (), (
override));
135 FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindowsView);
147 MockFlutterWindowsView*
view() {
return view_.get(); }
148 MockWindowBindingHandler*
window() {
return window_; }
151 FlutterWindowsEngineBuilder builder{GetContext()};
153 engine_ = builder.Build();
157 FlutterWindowsEngineBuilder builder{GetContext()};
159 auto window = std::make_unique<MockWindowBindingHandler>();
162 EXPECT_CALL(*window_, SetView).Times(1);
163 EXPECT_CALL(*
window, GetWindowHandle).WillRepeatedly(Return(
nullptr));
165 engine_ = builder.Build();
166 view_ = std::make_unique<MockFlutterWindowsView>(engine_.get(),
169 EngineModifier modifier{engine_.get()};
170 modifier.SetViewById(view_.get(), 456);
174 EXPECT_NE(engine_,
nullptr);
175 auto window = std::make_unique<MockWindowBindingHandler>();
176 EXPECT_CALL(*
window, SetView).Times(1);
177 EXPECT_CALL(*
window, GetWindowHandle).WillRepeatedly(Return(
nullptr));
178 auto view = std::make_unique<MockFlutterWindowsView>(engine_.get(),
181 EngineModifier modifier{engine_.get()};
182 modifier.SetViewById(view_.get(), view_id);
187 std::unique_ptr<FlutterWindowsEngine> engine_;
188 std::unique_ptr<MockFlutterWindowsView> view_;
189 MockWindowBindingHandler* window_;
197 auto handled_message = CreateResponse(
true);
198 auto unhandled_message = CreateResponse(
false);
199 int received_scancode = 0;
201 TestBinaryMessenger messenger(
202 [&received_scancode, &handled_message, &unhandled_message](
203 const std::string& channel,
const uint8_t*
message,
206 int redispatch_scancode = 0;
209 handler.
KeyboardHook(VK_RETURN, 100, WM_KEYDOWN,
'\n',
false,
false);
212 text.push_back(
'\n');
222 TestBinaryMessenger messenger([](
const std::string& channel,
223 const uint8_t*
message,
size_t message_size,
225 BinaryReply reply_handler = [](
const uint8_t* reply,
size_t reply_size) {};
231 EXPECT_CALL(*view(), OnResetImeComposing());
234 auto message = codec.EncodeMethodCall({
"TextInput.clearClient",
nullptr});
236 message->size(), reply_handler);
243 TestBinaryMessenger messenger([](
const std::string& channel,
244 const uint8_t*
message,
size_t message_size,
248 BinaryReply reply_handler = [&reply](
const uint8_t* reply_bytes,
250 reply = std::string(
reinterpret_cast<const char*
>(reply_bytes), reply_size);
256 auto message = codec.EncodeMethodCall({
"TextInput.clearClient",
nullptr});
258 message->size(), reply_handler);
262 "[\"Internal Consistency Error\",\"Text input is not available because "
263 "view with view_id=0 cannot be found\",null]");
271 bool sent_message =
false;
272 TestBinaryMessenger messenger(
273 [&sent_message](
const std::string& channel,
const uint8_t*
message,
276 BinaryReply reply_handler = [](
const uint8_t* reply,
size_t reply_size) {};
283 auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
284 auto& allocator = arguments->GetAllocator();
285 arguments->PushBack(kDefaultClientId, allocator);
286 rapidjson::Value config(rapidjson::kObjectType);
287 config.AddMember(
"inputAction",
"done", allocator);
288 config.AddMember(
"inputType",
"text", allocator);
290 config.AddMember(
kViewId, 456, allocator);
291 arguments->PushBack(config, allocator);
293 codec.EncodeMethodCall({
"TextInput.setClient", std::move(arguments)});
294 messenger.SimulateEngineMessage(
"flutter/textinput",
message->data(),
295 message->size(), reply_handler);
298 sent_message =
false;
300 EXPECT_TRUE(sent_message);
303 sent_message =
false;
305 EXPECT_TRUE(sent_message);
315 sent_message =
false;
317 EXPECT_FALSE(sent_message);
320 sent_message =
false;
322 EXPECT_TRUE(sent_message);
329 std::vector<std::string> messages;
331 TestBinaryMessenger messenger(
332 [&messages](
const std::string& channel,
const uint8_t*
message,
334 std::string last_message(
reinterpret_cast<const char*
>(
message),
336 messages.push_back(last_message);
338 BinaryReply reply_handler = [](
const uint8_t* reply,
size_t reply_size) {};
345 auto set_client_arguments =
346 EncodedClientConfig(
"TextInputType.multiline",
"TextInputAction.newline");
347 auto message = codec.EncodeMethodCall(
348 {
"TextInput.setClient", std::move(set_client_arguments)});
349 messenger.SimulateEngineMessage(
"flutter/textinput",
message->data(),
350 message->size(), reply_handler);
353 handler.
KeyboardHook(VK_RETURN, 100, WM_KEYDOWN,
'\n',
false,
false);
357 EXPECT_EQ(messages.size(), 2);
360 auto encoded_arguments = EncodedEditingState(
"\n",
TextRange(1));
361 auto update_state_message = codec.EncodeMethodCall(
364 EXPECT_TRUE(std::equal(update_state_message->begin(),
365 update_state_message->end(),
366 messages.front().begin()));
369 auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
370 auto& allocator = arguments->GetAllocator();
371 arguments->PushBack(kDefaultClientId, allocator);
373 rapidjson::Value(
"TextInputAction.newline", allocator).Move(), allocator);
374 auto invoke_action_message = codec.EncodeMethodCall(
375 {
"TextInputClient.performAction", std::move(arguments)});
377 EXPECT_TRUE(std::equal(invoke_action_message->begin(),
378 invoke_action_message->end(),
379 messages.back().begin()));
386 std::vector<std::vector<uint8_t>> messages;
388 TestBinaryMessenger messenger(
389 [&messages](
const std::string& channel,
const uint8_t*
message,
391 int length =
static_cast<int>(message_size);
392 std::vector<uint8_t> last_message(length);
393 memcpy(&last_message[0], &
message[0], length *
sizeof(uint8_t));
394 messages.push_back(last_message);
396 BinaryReply reply_handler = [](
const uint8_t* reply,
size_t reply_size) {};
403 auto set_client_arguments =
404 EncodedClientConfig(
"TextInputType.multiline",
"TextInputAction.send");
405 auto message = codec.EncodeMethodCall(
406 {
"TextInput.setClient", std::move(set_client_arguments)});
407 messenger.SimulateEngineMessage(
"flutter/textinput",
message->data(),
408 message->size(), reply_handler);
411 handler.
KeyboardHook(VK_RETURN, 100, WM_KEYDOWN,
'\n',
false,
false);
414 EXPECT_EQ(messages.size(), 1);
417 auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
418 auto& allocator = arguments->GetAllocator();
419 arguments->PushBack(kDefaultClientId, allocator);
421 rapidjson::Value(
"TextInputAction.send", allocator).Move(), allocator);
422 auto invoke_action_message = codec.EncodeMethodCall(
423 {
"TextInputClient.performAction", std::move(arguments)});
425 EXPECT_TRUE(std::equal(invoke_action_message->begin(),
426 invoke_action_message->end(),
427 messages.front().begin()));
433 TestBinaryMessenger messenger([](
const std::string& channel,
434 const uint8_t*
message,
size_t message_size,
439 auto args = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
440 auto& allocator = args->GetAllocator();
441 args->PushBack(123, allocator);
443 rapidjson::Value client_config(rapidjson::kObjectType);
445 args->PushBack(client_config, allocator);
450 BinaryReply reply_handler = [&reply](
const uint8_t* reply_bytes,
452 reply = std::string(
reinterpret_cast<const char*
>(reply_bytes), reply_size);
455 EXPECT_TRUE(messenger.SimulateEngineMessage(
kChannelName, encoded->data(),
456 encoded->size(), reply_handler));
459 "[\"Bad Arguments\",\"Could not set client, view ID is null.\",null]");
465 TestBinaryMessenger messenger([](
const std::string& channel,
466 const uint8_t*
message,
size_t message_size,
471 auto args = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
472 auto& allocator = args->GetAllocator();
473 args->PushBack(123, allocator);
475 rapidjson::Value client_config(rapidjson::kObjectType);
476 client_config.AddMember(
kViewId,
"Not an integer", allocator);
478 args->PushBack(client_config, allocator);
483 BinaryReply reply_handler = [&reply](
const uint8_t* reply_bytes,
485 reply = std::string(
reinterpret_cast<const char*
>(reply_bytes), reply_size);
488 EXPECT_TRUE(messenger.SimulateEngineMessage(
kChannelName, encoded->data(),
489 encoded->size(), reply_handler));
492 "[\"Bad Arguments\",\"Could not set client, view ID is null.\",null]");
498 auto handled_message = CreateResponse(
true);
499 auto unhandled_message = CreateResponse(
false);
500 int received_scancode = 0;
502 TestBinaryMessenger messenger(
503 [&received_scancode, &handled_message, &unhandled_message](
504 const std::string& channel,
const uint8_t*
message,
507 int redispatch_scancode = 0;
510 auto args = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
511 auto& allocator = args->GetAllocator();
512 args->PushBack(123, allocator);
514 rapidjson::Value client_config(rapidjson::kObjectType);
516 client_config.AddMember(
kViewId, 456, allocator);
518 args->PushBack(client_config, allocator);
522 EXPECT_TRUE(messenger.SimulateEngineMessage(
524 [](
const uint8_t* reply,
size_t reply_size) {}));
526 handler.
KeyboardHook(VK_RETURN, 100, WM_KEYDOWN,
'\n',
false,
false);
529 text.push_back(
'\n');
533 handler.
KeyboardHook(0x4E, 100, WM_KEYDOWN,
'n',
false,
false);
535 std::u16string textN;
538 handler.
KeyboardHook(0x49, 100, WM_KEYDOWN,
'i',
false,
false);
539 std::u16string textNi;
543 handler.
KeyboardHook(VK_RETURN, 100, WM_KEYDOWN,
'\n',
false,
false);
544 std::u16string textChineseCharacter;
545 text.push_back(u
'\u4F60');
557 int selection_base = -1;
558 TestBinaryMessenger messenger([&](
const std::string& channel,
559 const uint8_t*
message,
size_t size,
564 const auto& args = *method->arguments();
565 const auto& editing_state = args[1];
568 ASSERT_NE(base, editing_state.MemberEnd());
569 ASSERT_TRUE(base->value.IsInt());
570 ASSERT_NE(extent, editing_state.MemberEnd());
571 ASSERT_TRUE(extent->value.IsInt());
572 selection_base = base->value.GetInt();
573 EXPECT_EQ(extent->value.GetInt(), selection_base);
579 auto args = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
580 auto& allocator = args->GetAllocator();
581 args->PushBack(123, allocator);
582 rapidjson::Value client_config(rapidjson::kObjectType);
583 client_config.AddMember(
kViewId, 456, allocator);
584 args->PushBack(client_config, allocator);
587 EXPECT_TRUE(messenger.SimulateEngineMessage(
589 [](
const uint8_t* reply,
size_t reply_size) {}));
591 plugin.ComposeBeginHook();
592 EXPECT_EQ(selection_base, 0);
593 plugin.ComposeChangeHook(u
"abc", 3);
594 EXPECT_EQ(selection_base, 3);
596 plugin.ComposeCommitHook();
597 plugin.ComposeEndHook();
598 EXPECT_EQ(selection_base, 3);
600 plugin.ComposeBeginHook();
601 plugin.ComposeChangeHook(u
"1", 1);
602 EXPECT_EQ(selection_base, 4);
604 plugin.ComposeChangeHook(u
"12", 2);
605 EXPECT_EQ(selection_base, 5);
607 plugin.ComposeChangeHook(u
"12", 1);
608 EXPECT_EQ(selection_base, 4);
610 plugin.ComposeChangeHook(u
"12", 2);
611 EXPECT_EQ(selection_base, 5);
624 double ime_width = 50;
625 double ime_height = 60;
628 std::array<std::array<double, 4>, 4> editabletext_transform = {
629 1.0, 0.0, 0.0, view_x,
630 0.0, 1.0, 0.0, view_y,
634 TestBinaryMessenger messenger([](
const std::string& channel,
635 const uint8_t*
message,
size_t message_size,
637 BinaryReply reply_handler = [](
const uint8_t* reply,
size_t reply_size) {};
645 EXPECT_CALL(*view(), OnCursorRectUpdated(
Rect{{view_x, view_y}, {0, 0}}));
649 std::make_unique<rapidjson::Document>(rapidjson::kObjectType);
650 auto& allocator = arguments->GetAllocator();
652 rapidjson::Value transoform(rapidjson::kArrayType);
653 for (
int i = 0; i < 4 * 4; i++) {
655 transoform.PushBack(editabletext_transform[i % 4][i / 4], allocator);
658 arguments->AddMember(
"transform", transoform, allocator);
660 auto message = codec.EncodeMethodCall(
661 {
"TextInput.setEditableSizeAndTransform", std::move(arguments)});
663 message->size(), reply_handler);
667 OnCursorRectUpdated(
Rect{{view_x + ime_x, view_y + ime_y},
668 {ime_width, ime_height}}));
672 std::make_unique<rapidjson::Document>(rapidjson::kObjectType);
673 auto& allocator = arguments->GetAllocator();
675 arguments->AddMember(
"x", ime_x, allocator);
676 arguments->AddMember(
"y", ime_y, allocator);
677 arguments->AddMember(
"width", ime_width, allocator);
678 arguments->AddMember(
"height", ime_height, allocator);
680 auto message = codec.EncodeMethodCall(
681 {
"TextInput.setMarkedTextRect", std::move(arguments)});
683 message->size(), reply_handler);
690 TestBinaryMessenger messenger([](
const std::string& channel,
691 const uint8_t*
message,
size_t message_size,
695 BinaryReply reply_handler = [&reply](
const uint8_t* reply_bytes,
697 reply = std::string(
reinterpret_cast<const char*
>(reply_bytes), reply_size);
705 std::make_unique<rapidjson::Document>(rapidjson::kObjectType);
706 auto& allocator = arguments->GetAllocator();
708 arguments->AddMember(
"x", 0, allocator);
709 arguments->AddMember(
"y", 0, allocator);
710 arguments->AddMember(
"width", 0, allocator);
711 arguments->AddMember(
"height", 0, allocator);
713 auto message = codec.EncodeMethodCall(
714 {
"TextInput.setMarkedTextRect", std::move(arguments)});
716 message->size(), reply_handler);
720 "[\"Internal Consistency Error\",\"Text input is not available because "
721 "view with view_id=0 cannot be found\",null]");
728 bool sent_message =
false;
729 TestBinaryMessenger messenger(
730 [&sent_message](
const std::string& channel,
const uint8_t*
message,
736 auto const set_client_and_send_message = [&](
int client_id,
int view_id) {
737 auto args = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
738 auto& allocator = args->GetAllocator();
739 args->PushBack(client_id, allocator);
741 rapidjson::Value client_config(rapidjson::kObjectType);
742 client_config.AddMember(
kViewId, view_id, allocator);
744 args->PushBack(client_config, allocator);
749 BinaryReply reply_handler = [&reply](
const uint8_t* reply_bytes,
752 std::string(
reinterpret_cast<const char*
>(reply_bytes), reply_size);
755 EXPECT_TRUE(messenger.SimulateEngineMessage(
756 kChannelName, encoded->data(), encoded->size(), reply_handler));
758 sent_message =
false;
759 handler.ComposeBeginHook();
760 EXPECT_TRUE(sent_message);
761 sent_message =
false;
762 handler.ComposeChangeHook(u
"4", 1);
763 EXPECT_TRUE(sent_message);
764 sent_message =
false;
765 handler.ComposeCommitHook();
766 EXPECT_FALSE(sent_message);
767 sent_message =
false;
768 handler.ComposeEndHook();
769 EXPECT_TRUE(sent_message);
772 set_client_and_send_message(123, 456);
773 set_client_and_send_message(123, 789);