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