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/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 
22 namespace flutter {
23 namespace testing {
24 
25 namespace {
26 using ::testing::Return;
27 
28 static constexpr char kScanCodeKey[] = "scanCode";
29 static constexpr int kHandledScanCode = 20;
30 static constexpr int kUnhandledScanCode = 21;
31 static constexpr char kTextPlainFormat[] = "text/plain";
32 static constexpr int kDefaultClientId = 42;
33 // Should be identical to constants in text_input_plugin.cc.
34 static constexpr char kChannelName[] = "flutter/textinput";
35 static constexpr char kEnableDeltaModel[] = "enableDeltaModel";
36 static constexpr char kSetClientMethod[] = "TextInput.setClient";
37 static constexpr char kAffinityDownstream[] = "TextAffinity.downstream";
38 static constexpr char kTextKey[] = "text";
39 static constexpr char kSelectionBaseKey[] = "selectionBase";
40 static constexpr char kSelectionExtentKey[] = "selectionExtent";
41 static constexpr char kSelectionAffinityKey[] = "selectionAffinity";
42 static constexpr char kSelectionIsDirectionalKey[] = "selectionIsDirectional";
43 static constexpr char kComposingBaseKey[] = "composingBase";
44 static constexpr char kComposingExtentKey[] = "composingExtent";
45 static constexpr char kUpdateEditingStateMethod[] =
46  "TextInputClient.updateEditingState";
47 
48 static 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 
56 static 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 
74 static 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 
106 class MockFlutterWindowsView : public FlutterWindowsView {
107  public:
108  MockFlutterWindowsView(FlutterWindowsEngine* engine,
109  std::unique_ptr<WindowBindingHandler> window)
110  : FlutterWindowsView(kImplicitViewId, engine, std::move(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 
122 class TextInputPluginTest : public WindowsTest {
123  public:
124  TextInputPluginTest() = default;
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 
133  FlutterWindowsEngineBuilder builder{GetContext()};
134 
135  engine_ = builder.Build();
136  }
137 
139  FlutterWindowsEngineBuilder builder{GetContext()};
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_;
158  MockWindowBindingHandler* window_;
159 
160  FML_DISALLOW_COPY_AND_ASSIGN(TextInputPluginTest);
161 };
162 
163 TEST_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 
188 TEST_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.
207 TEST_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.
234 TEST_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 
290 TEST_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.
348 TEST_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 
395 TEST_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
453 TEST_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 
512 TEST_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 
583 TEST_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
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::kImplicitViewId
constexpr FlutterViewId kImplicitViewId
Definition: flutter_windows_engine.h:55
flutter::testing::TextInputPluginTest::~TextInputPluginTest
virtual ~TextInputPluginTest()=default
flutter::FlutterWindowsView::FlutterWindowsView
FlutterWindowsView(FlutterViewId view_id, FlutterWindowsEngine *engine, std::unique_ptr< WindowBindingHandler > window_binding, std::shared_ptr< WindowsProcTable > windows_proc_table=nullptr)
Definition: flutter_windows_view.cc:104
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:130
text_input_plugin.h
flutter::FlutterWindowsEngine
Definition: flutter_windows_engine.h:90
kUpdateEditingStateMethod
static constexpr char kUpdateEditingStateMethod[]
Definition: text_input_plugin.cc:28
flutter::testing::MockFlutterWindowsView::MockFlutterWindowsView
MockFlutterWindowsView(FlutterWindowsEngine *engine, std::unique_ptr< WindowBindingHandler > wbh)
Definition: flutter_windows_engine_unittests.cc:641
flutter::TextInputPlugin::ComposeEndHook
virtual void ComposeEndHook()
Definition: text_input_plugin.cc:175
flutter::testing::TextInputPluginTest::view
MockFlutterWindowsView * view()
Definition: text_input_plugin_unittest.cc:129
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::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:122
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:128
flutter::TextRange
Definition: text_range.h:19
flutter_windows_view.h
text
std::u16string text
Definition: keyboard_unittests.cc:332
flutter::MethodCall
Definition: method_call.h:18
flutter::testing::TextInputPluginTest::UseHeadlessEngine
void UseHeadlessEngine()
Definition: text_input_plugin_unittest.cc:132
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:138
flutter::testing::TEST_F
TEST_F(CompositorOpenGLTest, CreateBackingStore)
Definition: compositor_opengl_unittests.cc:125
flutter::MethodCodec::DecodeMethodCall
std::unique_ptr< MethodCall< T > > DecodeMethodCall(const uint8_t *message, size_t message_size) const
Definition: method_codec.h:32