Flutter Engine
The Flutter Engine
text_input_plugin_unittest.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4#include "flutter/shell/platform/windows/text_input_plugin.h"
5
6#include <rapidjson/document.h>
7#include <windows.h>
8#include <memory>
9
10#include "flutter/fml/macros.h"
11#include "flutter/shell/platform/common/json_message_codec.h"
12#include "flutter/shell/platform/common/json_method_codec.h"
13#include "flutter/shell/platform/windows/flutter_windows_view.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"
21
22namespace flutter {
23namespace testing {
24
25namespace {
26using ::testing::Return;
27
28static constexpr char kScanCodeKey[] = "scanCode";
29static constexpr int kHandledScanCode = 20;
30static constexpr int kUnhandledScanCode = 21;
31static constexpr char kTextPlainFormat[] = "text/plain";
32static constexpr int kDefaultClientId = 42;
33// Should be identical to constants in text_input_plugin.cc.
34static constexpr char kChannelName[] = "flutter/textinput";
35static constexpr char kEnableDeltaModel[] = "enableDeltaModel";
36static constexpr char kSetClientMethod[] = "TextInput.setClient";
37static constexpr char kAffinityDownstream[] = "TextAffinity.downstream";
38static constexpr char kTextKey[] = "text";
39static constexpr char kSelectionBaseKey[] = "selectionBase";
40static constexpr char kSelectionExtentKey[] = "selectionExtent";
41static constexpr char kSelectionAffinityKey[] = "selectionAffinity";
42static constexpr char kSelectionIsDirectionalKey[] = "selectionIsDirectional";
43static constexpr char kComposingBaseKey[] = "composingBase";
44static constexpr char kComposingExtentKey[] = "composingExtent";
45static constexpr char kUpdateEditingStateMethod[] =
46 "TextInputClient.updateEditingState";
47
48static std::unique_ptr<std::vector<uint8_t>> CreateResponse(bool handled) {
49 auto response_doc =
50 std::make_unique<rapidjson::Document>(rapidjson::kObjectType);
51 auto& allocator = response_doc->GetAllocator();
52 response_doc->AddMember("handled", handled, allocator);
53 return JsonMessageCodec::GetInstance().EncodeMessage(*response_doc);
54}
55
56static std::unique_ptr<rapidjson::Document> EncodedClientConfig(
57 std::string type_name,
58 std::string input_action) {
59 auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
60 auto& allocator = arguments->GetAllocator();
61 arguments->PushBack(kDefaultClientId, allocator);
62
63 rapidjson::Value config(rapidjson::kObjectType);
64 config.AddMember("inputAction", input_action, allocator);
65 config.AddMember(kEnableDeltaModel, false, allocator);
66 rapidjson::Value type_info(rapidjson::kObjectType);
67 type_info.AddMember("name", type_name, allocator);
68 config.AddMember("inputType", type_info, allocator);
69 arguments->PushBack(config, allocator);
70
71 return arguments;
72}
73
74static std::unique_ptr<rapidjson::Document> EncodedEditingState(
75 std::string text,
76 TextRange selection) {
77 auto model = std::make_unique<TextInputModel>();
78 model->SetText(text);
79 model->SetSelection(selection);
80
81 auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
82 auto& allocator = arguments->GetAllocator();
83 arguments->PushBack(kDefaultClientId, allocator);
84
85 rapidjson::Value editing_state(rapidjson::kObjectType);
86 editing_state.AddMember(kSelectionAffinityKey, kAffinityDownstream,
87 allocator);
88 editing_state.AddMember(kSelectionBaseKey, selection.base(), allocator);
89 editing_state.AddMember(kSelectionExtentKey, selection.extent(), allocator);
90 editing_state.AddMember(kSelectionIsDirectionalKey, false, allocator);
91
92 int composing_base =
93 model->composing() ? model->composing_range().base() : -1;
94 int composing_extent =
95 model->composing() ? model->composing_range().extent() : -1;
96 editing_state.AddMember(kComposingBaseKey, composing_base, allocator);
97 editing_state.AddMember(kComposingExtentKey, composing_extent, allocator);
98 editing_state.AddMember(kTextKey,
99 rapidjson::Value(model->GetText(), allocator).Move(),
100 allocator);
101 arguments->PushBack(editing_state, allocator);
102
103 return arguments;
104}
105
106class MockFlutterWindowsView : public FlutterWindowsView {
107 public:
108 MockFlutterWindowsView(FlutterWindowsEngine* engine,
109 std::unique_ptr<WindowBindingHandler> window)
111 virtual ~MockFlutterWindowsView() = default;
112
113 MOCK_METHOD(void, OnCursorRectUpdated, (const Rect&), (override));
114 MOCK_METHOD(void, OnResetImeComposing, (), (override));
115
116 private:
117 FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindowsView);
118};
119
120} // namespace
121
123 public:
125 virtual ~TextInputPluginTest() = default;
126
127 protected:
128 FlutterWindowsEngine* engine() { return engine_.get(); }
129 MockFlutterWindowsView* view() { return view_.get(); }
130 MockWindowBindingHandler* window() { return window_; }
131
134
135 engine_ = builder.Build();
136 }
137
140
141 auto window = std::make_unique<MockWindowBindingHandler>();
142
143 window_ = window.get();
144 EXPECT_CALL(*window_, SetView).Times(1);
145 EXPECT_CALL(*window, GetWindowHandle).WillRepeatedly(Return(nullptr));
146
147 engine_ = builder.Build();
148 view_ = std::make_unique<MockFlutterWindowsView>(engine_.get(),
149 std::move(window));
150
151 EngineModifier modifier{engine_.get()};
152 modifier.SetImplicitView(view_.get());
153 }
154
155 private:
156 std::unique_ptr<FlutterWindowsEngine> engine_;
157 std::unique_ptr<MockFlutterWindowsView> view_;
159
160 FML_DISALLOW_COPY_AND_ASSIGN(TextInputPluginTest);
161};
162
163TEST_F(TextInputPluginTest, TextMethodsWorksWithEmptyModel) {
164 UseEngineWithView();
165
166 auto handled_message = CreateResponse(true);
167 auto unhandled_message = CreateResponse(false);
168 int received_scancode = 0;
169
170 TestBinaryMessenger messenger(
171 [&received_scancode, &handled_message, &unhandled_message](
172 const std::string& channel, const uint8_t* message,
173 size_t message_size, BinaryReply reply) {});
174
175 int redispatch_scancode = 0;
176 TextInputPlugin handler(&messenger, engine());
177
178 handler.KeyboardHook(VK_RETURN, 100, WM_KEYDOWN, '\n', false, false);
179 handler.ComposeBeginHook();
180 std::u16string text;
181 text.push_back('\n');
182 handler.ComposeChangeHook(text, 1);
183 handler.ComposeEndHook();
184
185 // Passes if it did not crash
186}
187
188TEST_F(TextInputPluginTest, ClearClientResetsComposing) {
189 UseEngineWithView();
190
191 TestBinaryMessenger messenger([](const std::string& channel,
192 const uint8_t* message, size_t message_size,
193 BinaryReply reply) {});
194 BinaryReply reply_handler = [](const uint8_t* reply, size_t reply_size) {};
195
196 TextInputPlugin handler(&messenger, engine());
197
198 EXPECT_CALL(*view(), OnResetImeComposing());
199
200 auto& codec = JsonMethodCodec::GetInstance();
201 auto message = codec.EncodeMethodCall({"TextInput.clearClient", nullptr});
202 messenger.SimulateEngineMessage(kChannelName, message->data(),
203 message->size(), reply_handler);
204}
205
206// Verify that clear client fails if in headless mode.
207TEST_F(TextInputPluginTest, ClearClientRequiresView) {
208 UseHeadlessEngine();
209
210 TestBinaryMessenger messenger([](const std::string& channel,
211 const uint8_t* message, size_t message_size,
212 BinaryReply reply) {});
213
214 std::string reply;
215 BinaryReply reply_handler = [&reply](const uint8_t* reply_bytes,
216 size_t reply_size) {
217 reply = std::string(reinterpret_cast<const char*>(reply_bytes), reply_size);
218 };
219
220 TextInputPlugin handler(&messenger, engine());
221
222 auto& codec = JsonMethodCodec::GetInstance();
223 auto message = codec.EncodeMethodCall({"TextInput.clearClient", nullptr});
224 messenger.SimulateEngineMessage(kChannelName, message->data(),
225 message->size(), reply_handler);
226
227 EXPECT_EQ(reply,
228 "[\"Internal Consistency Error\",\"Text input is not available in "
229 "Windows headless mode\",null]");
230}
231
232// Verify that the embedder sends state update messages to the framework during
233// IME composing.
234TEST_F(TextInputPluginTest, VerifyComposingSendStateUpdate) {
235 UseEngineWithView();
236
237 bool sent_message = false;
238 TestBinaryMessenger messenger(
239 [&sent_message](const std::string& channel, const uint8_t* message,
240 size_t message_size,
241 BinaryReply reply) { sent_message = true; });
242 BinaryReply reply_handler = [](const uint8_t* reply, size_t reply_size) {};
243
244 TextInputPlugin handler(&messenger, engine());
245
246 auto& codec = JsonMethodCodec::GetInstance();
247
248 // Call TextInput.setClient to initialize the TextInputModel.
249 auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
250 auto& allocator = arguments->GetAllocator();
251 arguments->PushBack(kDefaultClientId, allocator);
252 rapidjson::Value config(rapidjson::kObjectType);
253 config.AddMember("inputAction", "done", allocator);
254 config.AddMember("inputType", "text", allocator);
255 config.AddMember(kEnableDeltaModel, false, allocator);
256 arguments->PushBack(config, allocator);
257 auto message =
258 codec.EncodeMethodCall({"TextInput.setClient", std::move(arguments)});
259 messenger.SimulateEngineMessage("flutter/textinput", message->data(),
260 message->size(), reply_handler);
261
262 // ComposeBeginHook should send state update.
263 sent_message = false;
264 handler.ComposeBeginHook();
265 EXPECT_TRUE(sent_message);
266
267 // ComposeChangeHook should send state update.
268 sent_message = false;
269 handler.ComposeChangeHook(u"4", 1);
270 EXPECT_TRUE(sent_message);
271
272 // ComposeCommitHook should NOT send state update.
273 //
274 // Commit messages are always immediately followed by a change message or an
275 // end message, both of which will send an update. Sending intermediate state
276 // with a collapsed composing region will trigger the framework to assume
277 // composing has ended, which is not the case until a WM_IME_ENDCOMPOSING
278 // event is received in the main event loop, which will trigger a call to
279 // ComposeEndHook.
280 sent_message = false;
281 handler.ComposeCommitHook();
282 EXPECT_FALSE(sent_message);
283
284 // ComposeEndHook should send state update.
285 sent_message = false;
286 handler.ComposeEndHook();
287 EXPECT_TRUE(sent_message);
288}
289
290TEST_F(TextInputPluginTest, VerifyInputActionNewlineInsertNewLine) {
291 UseEngineWithView();
292
293 // Store messages as std::string for convenience.
294 std::vector<std::string> messages;
295
296 TestBinaryMessenger messenger(
297 [&messages](const std::string& channel, const uint8_t* message,
298 size_t message_size, BinaryReply reply) {
299 std::string last_message(reinterpret_cast<const char*>(message),
300 message_size);
301 messages.push_back(last_message);
302 });
303 BinaryReply reply_handler = [](const uint8_t* reply, size_t reply_size) {};
304
305 TextInputPlugin handler(&messenger, engine());
306
307 auto& codec = JsonMethodCodec::GetInstance();
308
309 // Call TextInput.setClient to initialize the TextInputModel.
310 auto set_client_arguments =
311 EncodedClientConfig("TextInputType.multiline", "TextInputAction.newline");
312 auto message = codec.EncodeMethodCall(
313 {"TextInput.setClient", std::move(set_client_arguments)});
314 messenger.SimulateEngineMessage("flutter/textinput", message->data(),
315 message->size(), reply_handler);
316
317 // Simulate a key down event for '\n'.
318 handler.KeyboardHook(VK_RETURN, 100, WM_KEYDOWN, '\n', false, false);
319
320 // Two messages are expected, the first is TextInput.updateEditingState and
321 // the second is TextInputClient.performAction.
322 EXPECT_EQ(messages.size(), 2);
323
324 // Editing state should have been updated.
325 auto encoded_arguments = EncodedEditingState("\n", TextRange(1));
326 auto update_state_message = codec.EncodeMethodCall(
327 {kUpdateEditingStateMethod, std::move(encoded_arguments)});
328
329 EXPECT_TRUE(std::equal(update_state_message->begin(),
330 update_state_message->end(),
331 messages.front().begin()));
332
333 // TextInputClient.performAction should have been called.
334 auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
335 auto& allocator = arguments->GetAllocator();
336 arguments->PushBack(kDefaultClientId, allocator);
337 arguments->PushBack(
338 rapidjson::Value("TextInputAction.newline", allocator).Move(), allocator);
339 auto invoke_action_message = codec.EncodeMethodCall(
340 {"TextInputClient.performAction", std::move(arguments)});
341
342 EXPECT_TRUE(std::equal(invoke_action_message->begin(),
343 invoke_action_message->end(),
344 messages.back().begin()));
345}
346
347// Regression test for https://github.com/flutter/flutter/issues/125879.
348TEST_F(TextInputPluginTest, VerifyInputActionSendDoesNotInsertNewLine) {
349 UseEngineWithView();
350
351 std::vector<std::vector<uint8_t>> messages;
352
353 TestBinaryMessenger messenger(
354 [&messages](const std::string& channel, const uint8_t* message,
355 size_t message_size, BinaryReply reply) {
356 int length = static_cast<int>(message_size);
357 std::vector<uint8_t> last_message(length);
358 memcpy(&last_message[0], &message[0], length * sizeof(uint8_t));
359 messages.push_back(last_message);
360 });
361 BinaryReply reply_handler = [](const uint8_t* reply, size_t reply_size) {};
362
363 TextInputPlugin handler(&messenger, engine());
364
365 auto& codec = JsonMethodCodec::GetInstance();
366
367 // Call TextInput.setClient to initialize the TextInputModel.
368 auto set_client_arguments =
369 EncodedClientConfig("TextInputType.multiline", "TextInputAction.send");
370 auto message = codec.EncodeMethodCall(
371 {"TextInput.setClient", std::move(set_client_arguments)});
372 messenger.SimulateEngineMessage("flutter/textinput", message->data(),
373 message->size(), reply_handler);
374
375 // Simulate a key down event for '\n'.
376 handler.KeyboardHook(VK_RETURN, 100, WM_KEYDOWN, '\n', false, false);
377
378 // Only a call to TextInputClient.performAction is expected.
379 EXPECT_EQ(messages.size(), 1);
380
381 // TextInputClient.performAction should have been called.
382 auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
383 auto& allocator = arguments->GetAllocator();
384 arguments->PushBack(kDefaultClientId, allocator);
385 arguments->PushBack(
386 rapidjson::Value("TextInputAction.send", allocator).Move(), allocator);
387 auto invoke_action_message = codec.EncodeMethodCall(
388 {"TextInputClient.performAction", std::move(arguments)});
389
390 EXPECT_TRUE(std::equal(invoke_action_message->begin(),
391 invoke_action_message->end(),
392 messages.front().begin()));
393}
394
395TEST_F(TextInputPluginTest, TextEditingWorksWithDeltaModel) {
396 UseEngineWithView();
397
398 auto handled_message = CreateResponse(true);
399 auto unhandled_message = CreateResponse(false);
400 int received_scancode = 0;
401
402 TestBinaryMessenger messenger(
403 [&received_scancode, &handled_message, &unhandled_message](
404 const std::string& channel, const uint8_t* message,
405 size_t message_size, BinaryReply reply) {});
406
407 int redispatch_scancode = 0;
408 TextInputPlugin handler(&messenger, engine());
409
410 auto args = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
411 auto& allocator = args->GetAllocator();
412 args->PushBack(123, allocator); // client_id
413
414 rapidjson::Value client_config(rapidjson::kObjectType);
415 client_config.AddMember(kEnableDeltaModel, true, allocator);
416
417 args->PushBack(client_config, allocator);
420
421 EXPECT_TRUE(messenger.SimulateEngineMessage(
422 kChannelName, encoded->data(), encoded->size(),
423 [](const uint8_t* reply, size_t reply_size) {}));
424
425 handler.KeyboardHook(VK_RETURN, 100, WM_KEYDOWN, '\n', false, false);
426 handler.ComposeBeginHook();
427 std::u16string text;
428 text.push_back('\n');
429 handler.ComposeChangeHook(text, 1);
430 handler.ComposeEndHook();
431
432 handler.KeyboardHook(0x4E, 100, WM_KEYDOWN, 'n', false, false);
433 handler.ComposeBeginHook();
434 std::u16string textN;
435 text.push_back('n');
436 handler.ComposeChangeHook(textN, 1);
437 handler.KeyboardHook(0x49, 100, WM_KEYDOWN, 'i', false, false);
438 std::u16string textNi;
439 text.push_back('n');
440 text.push_back('i');
441 handler.ComposeChangeHook(textNi, 2);
442 handler.KeyboardHook(VK_RETURN, 100, WM_KEYDOWN, '\n', false, false);
443 std::u16string textChineseCharacter;
444 text.push_back(u'\u4F60');
445 handler.ComposeChangeHook(textChineseCharacter, 1);
446 handler.ComposeCommitHook();
447 handler.ComposeEndHook();
448
449 // Passes if it did not crash
450}
451
452// Regression test for https://github.com/flutter/flutter/issues/123749
453TEST_F(TextInputPluginTest, CompositionCursorPos) {
454 UseEngineWithView();
455
456 int selection_base = -1;
457 TestBinaryMessenger messenger([&](const std::string& channel,
458 const uint8_t* message, size_t size,
459 BinaryReply reply) {
461 std::vector<uint8_t>(message, message + size));
462 if (method->method_name() == kUpdateEditingStateMethod) {
463 const auto& args = *method->arguments();
464 const auto& editing_state = args[1];
465 auto base = editing_state.FindMember(kSelectionBaseKey);
466 auto extent = editing_state.FindMember(kSelectionExtentKey);
467 ASSERT_NE(base, editing_state.MemberEnd());
468 ASSERT_TRUE(base->value.IsInt());
469 ASSERT_NE(extent, editing_state.MemberEnd());
470 ASSERT_TRUE(extent->value.IsInt());
471 selection_base = base->value.GetInt();
472 EXPECT_EQ(extent->value.GetInt(), selection_base);
473 }
474 });
475
476 TextInputPlugin plugin(&messenger, engine());
477
478 auto args = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
479 auto& allocator = args->GetAllocator();
480 args->PushBack(123, allocator); // client_id
481 rapidjson::Value client_config(rapidjson::kObjectType);
482 args->PushBack(client_config, allocator);
485 EXPECT_TRUE(messenger.SimulateEngineMessage(
486 kChannelName, encoded->data(), encoded->size(),
487 [](const uint8_t* reply, size_t reply_size) {}));
488
489 plugin.ComposeBeginHook();
490 EXPECT_EQ(selection_base, 0);
491 plugin.ComposeChangeHook(u"abc", 3);
492 EXPECT_EQ(selection_base, 3);
493
494 plugin.ComposeCommitHook();
495 plugin.ComposeEndHook();
496 EXPECT_EQ(selection_base, 3);
497
498 plugin.ComposeBeginHook();
499 plugin.ComposeChangeHook(u"1", 1);
500 EXPECT_EQ(selection_base, 4);
501
502 plugin.ComposeChangeHook(u"12", 2);
503 EXPECT_EQ(selection_base, 5);
504
505 plugin.ComposeChangeHook(u"12", 1);
506 EXPECT_EQ(selection_base, 4);
507
508 plugin.ComposeChangeHook(u"12", 2);
509 EXPECT_EQ(selection_base, 5);
510}
511
512TEST_F(TextInputPluginTest, TransformCursorRect) {
513 UseEngineWithView();
514
515 // A position of `EditableText`.
516 double view_x = 100;
517 double view_y = 200;
518
519 // A position and size of marked text, in `EditableText` local coordinates.
520 double ime_x = 3;
521 double ime_y = 4;
522 double ime_width = 50;
523 double ime_height = 60;
524
525 // Transformation matrix.
526 std::array<std::array<double, 4>, 4> editabletext_transform = {
527 1.0, 0.0, 0.0, view_x, //
528 0.0, 1.0, 0.0, view_y, //
529 0.0, 0.0, 0.0, 0.0, //
530 0.0, 0.0, 0.0, 1.0};
531
532 TestBinaryMessenger messenger([](const std::string& channel,
533 const uint8_t* message, size_t message_size,
534 BinaryReply reply) {});
535 BinaryReply reply_handler = [](const uint8_t* reply, size_t reply_size) {};
536
537 TextInputPlugin handler(&messenger, engine());
538
539 auto& codec = JsonMethodCodec::GetInstance();
540
541 EXPECT_CALL(*view(), OnCursorRectUpdated(Rect{{view_x, view_y}, {0, 0}}));
542
543 {
544 auto arguments =
545 std::make_unique<rapidjson::Document>(rapidjson::kObjectType);
546 auto& allocator = arguments->GetAllocator();
547
548 rapidjson::Value transoform(rapidjson::kArrayType);
549 for (int i = 0; i < 4 * 4; i++) {
550 // Pack 2-dimensional array by column-major order.
551 transoform.PushBack(editabletext_transform[i % 4][i / 4], allocator);
552 }
553
554 arguments->AddMember("transform", transoform, allocator);
555
556 auto message = codec.EncodeMethodCall(
557 {"TextInput.setEditableSizeAndTransform", std::move(arguments)});
558 messenger.SimulateEngineMessage(kChannelName, message->data(),
559 message->size(), reply_handler);
560 }
561
562 EXPECT_CALL(*view(),
563 OnCursorRectUpdated(Rect{{view_x + ime_x, view_y + ime_y},
564 {ime_width, ime_height}}));
565
566 {
567 auto arguments =
568 std::make_unique<rapidjson::Document>(rapidjson::kObjectType);
569 auto& allocator = arguments->GetAllocator();
570
571 arguments->AddMember("x", ime_x, allocator);
572 arguments->AddMember("y", ime_y, allocator);
573 arguments->AddMember("width", ime_width, allocator);
574 arguments->AddMember("height", ime_height, allocator);
575
576 auto message = codec.EncodeMethodCall(
577 {"TextInput.setMarkedTextRect", std::move(arguments)});
578 messenger.SimulateEngineMessage(kChannelName, message->data(),
579 message->size(), reply_handler);
580 }
581}
582
583TEST_F(TextInputPluginTest, SetMarkedTextRectRequiresView) {
584 UseHeadlessEngine();
585
586 TestBinaryMessenger messenger([](const std::string& channel,
587 const uint8_t* message, size_t message_size,
588 BinaryReply reply) {});
589
590 std::string reply;
591 BinaryReply reply_handler = [&reply](const uint8_t* reply_bytes,
592 size_t reply_size) {
593 reply = std::string(reinterpret_cast<const char*>(reply_bytes), reply_size);
594 };
595
596 TextInputPlugin handler(&messenger, engine());
597
598 auto& codec = JsonMethodCodec::GetInstance();
599
600 auto arguments =
601 std::make_unique<rapidjson::Document>(rapidjson::kObjectType);
602 auto& allocator = arguments->GetAllocator();
603
604 arguments->AddMember("x", 0, allocator);
605 arguments->AddMember("y", 0, allocator);
606 arguments->AddMember("width", 0, allocator);
607 arguments->AddMember("height", 0, allocator);
608
609 auto message = codec.EncodeMethodCall(
610 {"TextInput.setMarkedTextRect", std::move(arguments)});
611 messenger.SimulateEngineMessage(kChannelName, message->data(),
612 message->size(), reply_handler);
613
614 EXPECT_EQ(reply,
615 "[\"Internal Consistency Error\",\"Text input is not available in "
616 "Windows headless mode\",null]");
617}
618
619} // namespace testing
620} // namespace flutter
static NSString *const kChannelName
static bool equal(const SkBitmap &a, const SkBitmap &b)
Definition: ImageTest.cpp:1395
FlutterWindowsView(FlutterViewId view_id, FlutterWindowsEngine *engine, std::unique_ptr< WindowBindingHandler > window_binding, std::shared_ptr< WindowsProcTable > windows_proc_table=nullptr)
static const JsonMessageCodec & GetInstance()
static const JsonMethodCodec & GetInstance()
std::unique_ptr< std::vector< uint8_t > > EncodeMessage(const T &message) const
Definition: message_codec.h:45
std::unique_ptr< MethodCall< T > > DecodeMethodCall(const uint8_t *message, size_t message_size) const
Definition: method_codec.h:32
std::unique_ptr< std::vector< uint8_t > > EncodeMethodCall(const MethodCall< T > &method_call) const
Definition: method_codec.h:48
void KeyboardHook(GLFWwindow *window, int key, int scancode, int action, int mods) override
virtual void ComposeChangeHook(const std::u16string &text, int cursor_pos)
MOCK_METHOD(void, NotifyWinEventWrapper,(ui::AXPlatformNodeWin *, ax::mojom::Event),(override))
MockFlutterWindowsView(FlutterWindowsEngine *engine, std::unique_ptr< WindowBindingHandler > wbh)
WindowsTestContext & GetContext()
Definition: windows_test.cc:21
GLFWwindow * window
Definition: main.cc:45
FlutterEngine engine
Definition: main.cc:68
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
#define FML_DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition: macros.h:27
static constexpr char kUpdateEditingStateMethod[]
static constexpr char kAffinityDownstream[]
static NSString *const kEnableDeltaModel
static NSString *const kSetClientMethod
static constexpr char kScanCodeKey[]
size_t length
std::u16string text
Win32Message message
constexpr char kTextPlainFormat[]
Clipboard plain text format.
static NSString *const kTextKey
static NSString *const kSelectionBaseKey
static NSString *const kSelectionAffinityKey
static NSString *const kComposingExtentKey
static NSString *const kSelectionExtentKey
static NSString *const kComposingBaseKey
static NSString *const kSelectionIsDirectionalKey
TEST_F(DisplayListTest, Defaults)
constexpr int64_t kImplicitViewId
std::function< void(const uint8_t *reply, size_t reply_size)> BinaryReply
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
TRect< Scalar > Rect
Definition: rect.h:769
SkRange< size_t > TextRange
Definition: TextStyle.h:337
Definition: ref_ptr.h:256
#define EXPECT_TRUE(handle)
Definition: unit_test.h:678