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"
13 #include "flutter/shell/platform/windows/testing/test_binary_messenger.h"
15 #include "gmock/gmock.h"
16 #include "gtest/gtest.h"
17 
18 namespace flutter {
19 namespace testing {
20 
21 namespace {
22 static constexpr char kScanCodeKey[] = "scanCode";
23 static constexpr int kHandledScanCode = 20;
24 static constexpr int kUnhandledScanCode = 21;
25 static constexpr char kTextPlainFormat[] = "text/plain";
26 static constexpr int kDefaultClientId = 42;
27 // Should be identical to constants in text_input_plugin.cc.
28 static constexpr char kChannelName[] = "flutter/textinput";
29 static constexpr char kEnableDeltaModel[] = "enableDeltaModel";
30 static constexpr char kSetClientMethod[] = "TextInput.setClient";
31 static constexpr char kAffinityDownstream[] = "TextAffinity.downstream";
32 static constexpr char kTextKey[] = "text";
33 static constexpr char kSelectionBaseKey[] = "selectionBase";
34 static constexpr char kSelectionExtentKey[] = "selectionExtent";
35 static constexpr char kSelectionAffinityKey[] = "selectionAffinity";
36 static constexpr char kSelectionIsDirectionalKey[] = "selectionIsDirectional";
37 static constexpr char kComposingBaseKey[] = "composingBase";
38 static constexpr char kComposingExtentKey[] = "composingExtent";
39 static constexpr char kUpdateEditingStateMethod[] =
40  "TextInputClient.updateEditingState";
41 
42 static std::unique_ptr<std::vector<uint8_t>> CreateResponse(bool handled) {
43  auto response_doc =
44  std::make_unique<rapidjson::Document>(rapidjson::kObjectType);
45  auto& allocator = response_doc->GetAllocator();
46  response_doc->AddMember("handled", handled, allocator);
47  return JsonMessageCodec::GetInstance().EncodeMessage(*response_doc);
48 }
49 
50 static std::unique_ptr<rapidjson::Document> EncodedClientConfig(
51  std::string type_name,
52  std::string input_action) {
53  auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
54  auto& allocator = arguments->GetAllocator();
55  arguments->PushBack(kDefaultClientId, allocator);
56 
57  rapidjson::Value config(rapidjson::kObjectType);
58  config.AddMember("inputAction", input_action, allocator);
59  config.AddMember(kEnableDeltaModel, false, allocator);
60  rapidjson::Value type_info(rapidjson::kObjectType);
61  type_info.AddMember("name", type_name, allocator);
62  config.AddMember("inputType", type_info, allocator);
63  arguments->PushBack(config, allocator);
64 
65  return arguments;
66 }
67 
68 static std::unique_ptr<rapidjson::Document> EncodedEditingState(
69  std::string text,
70  TextRange selection) {
71  auto model = std::make_unique<TextInputModel>();
72  model->SetText(text);
73  model->SetSelection(selection);
74 
75  auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
76  auto& allocator = arguments->GetAllocator();
77  arguments->PushBack(kDefaultClientId, allocator);
78 
79  rapidjson::Value editing_state(rapidjson::kObjectType);
80  editing_state.AddMember(kSelectionAffinityKey, kAffinityDownstream,
81  allocator);
82  editing_state.AddMember(kSelectionBaseKey, selection.base(), allocator);
83  editing_state.AddMember(kSelectionExtentKey, selection.extent(), allocator);
84  editing_state.AddMember(kSelectionIsDirectionalKey, false, allocator);
85 
86  int composing_base =
87  model->composing() ? model->composing_range().base() : -1;
88  int composing_extent =
89  model->composing() ? model->composing_range().extent() : -1;
90  editing_state.AddMember(kComposingBaseKey, composing_base, allocator);
91  editing_state.AddMember(kComposingExtentKey, composing_extent, allocator);
92  editing_state.AddMember(kTextKey,
93  rapidjson::Value(model->GetText(), allocator).Move(),
94  allocator);
95  arguments->PushBack(editing_state, allocator);
96 
97  return arguments;
98 }
99 
100 class MockTextInputPluginDelegate : public TextInputPluginDelegate {
101  public:
102  MockTextInputPluginDelegate() {}
103  virtual ~MockTextInputPluginDelegate() = default;
104 
105  MOCK_METHOD(void, OnCursorRectUpdated, (const Rect&), (override));
106  MOCK_METHOD(void, OnResetImeComposing, (), (override));
107 
108  private:
109  FML_DISALLOW_COPY_AND_ASSIGN(MockTextInputPluginDelegate);
110 };
111 
112 } // namespace
113 
114 TEST(TextInputPluginTest, TextMethodsWorksWithEmptyModel) {
115  auto handled_message = CreateResponse(true);
116  auto unhandled_message = CreateResponse(false);
117  int received_scancode = 0;
118 
119  TestBinaryMessenger messenger(
120  [&received_scancode, &handled_message, &unhandled_message](
121  const std::string& channel, const uint8_t* message,
122  size_t message_size, BinaryReply reply) {});
123  MockTextInputPluginDelegate delegate;
124 
125  int redispatch_scancode = 0;
126  TextInputPlugin handler(&messenger, &delegate);
127 
128  handler.KeyboardHook(VK_RETURN, 100, WM_KEYDOWN, '\n', false, false);
129  handler.ComposeBeginHook();
130  std::u16string text;
131  text.push_back('\n');
132  handler.ComposeChangeHook(text, 1);
133  handler.ComposeEndHook();
134 
135  // Passes if it did not crash
136 }
137 
138 TEST(TextInputPluginTest, ClearClientResetsComposing) {
139  TestBinaryMessenger messenger([](const std::string& channel,
140  const uint8_t* message, size_t message_size,
141  BinaryReply reply) {});
142  BinaryReply reply_handler = [](const uint8_t* reply, size_t reply_size) {};
143 
144  MockTextInputPluginDelegate delegate;
145  TextInputPlugin handler(&messenger, &delegate);
146 
147  EXPECT_CALL(delegate, OnResetImeComposing());
148 
149  auto& codec = JsonMethodCodec::GetInstance();
150  auto message = codec.EncodeMethodCall({"TextInput.clearClient", nullptr});
151  messenger.SimulateEngineMessage(kChannelName, message->data(),
152  message->size(), reply_handler);
153 }
154 
155 // Verify that the embedder sends state update messages to the framework during
156 // IME composing.
157 TEST(TextInputPluginTest, VerifyComposingSendStateUpdate) {
158  bool sent_message = false;
159  TestBinaryMessenger messenger(
160  [&sent_message](const std::string& channel, const uint8_t* message,
161  size_t message_size,
162  BinaryReply reply) { sent_message = true; });
163  BinaryReply reply_handler = [](const uint8_t* reply, size_t reply_size) {};
164 
165  MockTextInputPluginDelegate delegate;
166  TextInputPlugin handler(&messenger, &delegate);
167 
168  auto& codec = JsonMethodCodec::GetInstance();
169 
170  // Call TextInput.setClient to initialize the TextInputModel.
171  auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
172  auto& allocator = arguments->GetAllocator();
173  arguments->PushBack(kDefaultClientId, allocator);
174  rapidjson::Value config(rapidjson::kObjectType);
175  config.AddMember("inputAction", "done", allocator);
176  config.AddMember("inputType", "text", allocator);
177  config.AddMember(kEnableDeltaModel, false, allocator);
178  arguments->PushBack(config, allocator);
179  auto message =
180  codec.EncodeMethodCall({"TextInput.setClient", std::move(arguments)});
181  messenger.SimulateEngineMessage("flutter/textinput", message->data(),
182  message->size(), reply_handler);
183 
184  // ComposeBeginHook should send state update.
185  sent_message = false;
186  handler.ComposeBeginHook();
187  EXPECT_TRUE(sent_message);
188 
189  // ComposeChangeHook should send state update.
190  sent_message = false;
191  handler.ComposeChangeHook(u"4", 1);
192  EXPECT_TRUE(sent_message);
193 
194  // ComposeCommitHook should NOT send state update.
195  //
196  // Commit messages are always immediately followed by a change message or an
197  // end message, both of which will send an update. Sending intermediate state
198  // with a collapsed composing region will trigger the framework to assume
199  // composing has ended, which is not the case until a WM_IME_ENDCOMPOSING
200  // event is received in the main event loop, which will trigger a call to
201  // ComposeEndHook.
202  sent_message = false;
203  handler.ComposeCommitHook();
204  EXPECT_FALSE(sent_message);
205 
206  // ComposeEndHook should send state update.
207  sent_message = false;
208  handler.ComposeEndHook();
209  EXPECT_TRUE(sent_message);
210 }
211 
212 TEST(TextInputPluginTest, VerifyInputActionNewlineInsertNewLine) {
213  // Store messages as std::string for convenience.
214  std::vector<std::string> messages;
215 
216  TestBinaryMessenger messenger(
217  [&messages](const std::string& channel, const uint8_t* message,
218  size_t message_size, BinaryReply reply) {
219  std::string last_message(reinterpret_cast<const char*>(message),
220  message_size);
221  messages.push_back(last_message);
222  });
223  BinaryReply reply_handler = [](const uint8_t* reply, size_t reply_size) {};
224 
225  MockTextInputPluginDelegate delegate;
226  TextInputPlugin handler(&messenger, &delegate);
227 
228  auto& codec = JsonMethodCodec::GetInstance();
229 
230  // Call TextInput.setClient to initialize the TextInputModel.
231  auto set_client_arguments =
232  EncodedClientConfig("TextInputType.multiline", "TextInputAction.newline");
233  auto message = codec.EncodeMethodCall(
234  {"TextInput.setClient", std::move(set_client_arguments)});
235  messenger.SimulateEngineMessage("flutter/textinput", message->data(),
236  message->size(), reply_handler);
237 
238  // Simulate a key down event for '\n'.
239  handler.KeyboardHook(VK_RETURN, 100, WM_KEYDOWN, '\n', false, false);
240 
241  // Two messages are expected, the first is TextInput.updateEditingState and
242  // the second is TextInputClient.performAction.
243  EXPECT_EQ(messages.size(), 2);
244 
245  // Editing state should have been updated.
246  auto encoded_arguments = EncodedEditingState("\n", TextRange(1));
247  auto update_state_message = codec.EncodeMethodCall(
248  {kUpdateEditingStateMethod, std::move(encoded_arguments)});
249 
250  EXPECT_TRUE(std::equal(update_state_message->begin(),
251  update_state_message->end(),
252  messages.front().begin()));
253 
254  // TextInputClient.performAction should have been called.
255  auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
256  auto& allocator = arguments->GetAllocator();
257  arguments->PushBack(kDefaultClientId, allocator);
258  arguments->PushBack(
259  rapidjson::Value("TextInputAction.newline", allocator).Move(), allocator);
260  auto invoke_action_message = codec.EncodeMethodCall(
261  {"TextInputClient.performAction", std::move(arguments)});
262 
263  EXPECT_TRUE(std::equal(invoke_action_message->begin(),
264  invoke_action_message->end(),
265  messages.back().begin()));
266 }
267 
268 // Regression test for https://github.com/flutter/flutter/issues/125879.
269 TEST(TextInputPluginTest, VerifyInputActionSendDoesNotInsertNewLine) {
270  std::vector<std::vector<uint8_t>> messages;
271 
272  TestBinaryMessenger messenger(
273  [&messages](const std::string& channel, const uint8_t* message,
274  size_t message_size, BinaryReply reply) {
275  int length = static_cast<int>(message_size);
276  std::vector<uint8_t> last_message(length);
277  memcpy(&last_message[0], &message[0], length * sizeof(uint8_t));
278  messages.push_back(last_message);
279  });
280  BinaryReply reply_handler = [](const uint8_t* reply, size_t reply_size) {};
281 
282  MockTextInputPluginDelegate delegate;
283  TextInputPlugin handler(&messenger, &delegate);
284 
285  auto& codec = JsonMethodCodec::GetInstance();
286 
287  // Call TextInput.setClient to initialize the TextInputModel.
288  auto set_client_arguments =
289  EncodedClientConfig("TextInputType.multiline", "TextInputAction.send");
290  auto message = codec.EncodeMethodCall(
291  {"TextInput.setClient", std::move(set_client_arguments)});
292  messenger.SimulateEngineMessage("flutter/textinput", message->data(),
293  message->size(), reply_handler);
294 
295  // Simulate a key down event for '\n'.
296  handler.KeyboardHook(VK_RETURN, 100, WM_KEYDOWN, '\n', false, false);
297 
298  // Only a call to TextInputClient.performAction is expected.
299  EXPECT_EQ(messages.size(), 1);
300 
301  // TextInputClient.performAction should have been called.
302  auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
303  auto& allocator = arguments->GetAllocator();
304  arguments->PushBack(kDefaultClientId, allocator);
305  arguments->PushBack(
306  rapidjson::Value("TextInputAction.send", allocator).Move(), allocator);
307  auto invoke_action_message = codec.EncodeMethodCall(
308  {"TextInputClient.performAction", std::move(arguments)});
309 
310  EXPECT_TRUE(std::equal(invoke_action_message->begin(),
311  invoke_action_message->end(),
312  messages.front().begin()));
313 }
314 
315 TEST(TextInputPluginTest, TextEditingWorksWithDeltaModel) {
316  auto handled_message = CreateResponse(true);
317  auto unhandled_message = CreateResponse(false);
318  int received_scancode = 0;
319 
320  TestBinaryMessenger messenger(
321  [&received_scancode, &handled_message, &unhandled_message](
322  const std::string& channel, const uint8_t* message,
323  size_t message_size, BinaryReply reply) {});
324  MockTextInputPluginDelegate delegate;
325 
326  int redispatch_scancode = 0;
327  TextInputPlugin handler(&messenger, &delegate);
328 
329  auto args = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
330  auto& allocator = args->GetAllocator();
331  args->PushBack(123, allocator); // client_id
332 
333  rapidjson::Value client_config(rapidjson::kObjectType);
334  client_config.AddMember(kEnableDeltaModel, true, allocator);
335 
336  args->PushBack(client_config, allocator);
339 
340  EXPECT_TRUE(messenger.SimulateEngineMessage(
341  kChannelName, encoded->data(), encoded->size(),
342  [](const uint8_t* reply, size_t reply_size) {}));
343 
344  handler.KeyboardHook(VK_RETURN, 100, WM_KEYDOWN, '\n', false, false);
345  handler.ComposeBeginHook();
346  std::u16string text;
347  text.push_back('\n');
348  handler.ComposeChangeHook(text, 1);
349  handler.ComposeEndHook();
350 
351  handler.KeyboardHook(0x4E, 100, WM_KEYDOWN, 'n', false, false);
352  handler.ComposeBeginHook();
353  std::u16string textN;
354  text.push_back('n');
355  handler.ComposeChangeHook(textN, 1);
356  handler.KeyboardHook(0x49, 100, WM_KEYDOWN, 'i', false, false);
357  std::u16string textNi;
358  text.push_back('n');
359  text.push_back('i');
360  handler.ComposeChangeHook(textNi, 2);
361  handler.KeyboardHook(VK_RETURN, 100, WM_KEYDOWN, '\n', false, false);
362  std::u16string textChineseCharacter;
363  text.push_back(u'\u4F60');
364  handler.ComposeChangeHook(textChineseCharacter, 1);
365  handler.ComposeCommitHook();
366  handler.ComposeEndHook();
367 
368  // Passes if it did not crash
369 }
370 
371 // Regression test for https://github.com/flutter/flutter/issues/123749
372 TEST(TextInputPluginTest, CompositionCursorPos) {
373  int selection_base = -1;
374  TestBinaryMessenger messenger([&](const std::string& channel,
375  const uint8_t* message, size_t size,
376  BinaryReply reply) {
378  std::vector<uint8_t>(message, message + size));
379  if (method->method_name() == kUpdateEditingStateMethod) {
380  const auto& args = *method->arguments();
381  const auto& editing_state = args[1];
382  auto base = editing_state.FindMember(kSelectionBaseKey);
383  auto extent = editing_state.FindMember(kSelectionExtentKey);
384  ASSERT_NE(base, editing_state.MemberEnd());
385  ASSERT_TRUE(base->value.IsInt());
386  ASSERT_NE(extent, editing_state.MemberEnd());
387  ASSERT_TRUE(extent->value.IsInt());
388  selection_base = base->value.GetInt();
389  EXPECT_EQ(extent->value.GetInt(), selection_base);
390  }
391  });
392  MockTextInputPluginDelegate delegate;
393 
394  TextInputPlugin plugin(&messenger, &delegate);
395 
396  auto args = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
397  auto& allocator = args->GetAllocator();
398  args->PushBack(123, allocator); // client_id
399  rapidjson::Value client_config(rapidjson::kObjectType);
400  args->PushBack(client_config, allocator);
403  EXPECT_TRUE(messenger.SimulateEngineMessage(
404  kChannelName, encoded->data(), encoded->size(),
405  [](const uint8_t* reply, size_t reply_size) {}));
406 
407  plugin.ComposeBeginHook();
408  EXPECT_EQ(selection_base, 0);
409  plugin.ComposeChangeHook(u"abc", 3);
410  EXPECT_EQ(selection_base, 3);
411 
412  plugin.ComposeCommitHook();
413  plugin.ComposeEndHook();
414  EXPECT_EQ(selection_base, 3);
415 
416  plugin.ComposeBeginHook();
417  plugin.ComposeChangeHook(u"1", 1);
418  EXPECT_EQ(selection_base, 4);
419 
420  plugin.ComposeChangeHook(u"12", 2);
421  EXPECT_EQ(selection_base, 5);
422 
423  plugin.ComposeChangeHook(u"12", 1);
424  EXPECT_EQ(selection_base, 4);
425 
426  plugin.ComposeChangeHook(u"12", 2);
427  EXPECT_EQ(selection_base, 5);
428 }
429 
430 TEST(TextInputPluginTest, TransformCursorRect) {
431  // A position of `EditableText`.
432  double view_x = 100;
433  double view_y = 200;
434 
435  // A position and size of marked text, in `EditableText` local coordinates.
436  double ime_x = 3;
437  double ime_y = 4;
438  double ime_width = 50;
439  double ime_height = 60;
440 
441  // Transformation matrix.
442  std::array<std::array<double, 4>, 4> editabletext_transform = {
443  1.0, 0.0, 0.0, view_x, //
444  0.0, 1.0, 0.0, view_y, //
445  0.0, 0.0, 0.0, 0.0, //
446  0.0, 0.0, 0.0, 1.0};
447 
448  TestBinaryMessenger messenger([](const std::string& channel,
449  const uint8_t* message, size_t message_size,
450  BinaryReply reply) {});
451  BinaryReply reply_handler = [](const uint8_t* reply, size_t reply_size) {};
452 
453  MockTextInputPluginDelegate delegate;
454  TextInputPlugin handler(&messenger, &delegate);
455 
456  auto& codec = JsonMethodCodec::GetInstance();
457 
458  EXPECT_CALL(delegate, OnCursorRectUpdated(Rect{{view_x, view_y}, {0, 0}}));
459 
460  {
461  auto arguments =
462  std::make_unique<rapidjson::Document>(rapidjson::kObjectType);
463  auto& allocator = arguments->GetAllocator();
464 
465  rapidjson::Value transoform(rapidjson::kArrayType);
466  for (int i = 0; i < 4 * 4; i++) {
467  // Pack 2-dimensional array by column-major order.
468  transoform.PushBack(editabletext_transform[i % 4][i / 4], allocator);
469  }
470 
471  arguments->AddMember("transform", transoform, allocator);
472 
473  auto message = codec.EncodeMethodCall(
474  {"TextInput.setEditableSizeAndTransform", std::move(arguments)});
475  messenger.SimulateEngineMessage(kChannelName, message->data(),
476  message->size(), reply_handler);
477  }
478 
479  EXPECT_CALL(delegate,
480  OnCursorRectUpdated(Rect{{view_x + ime_x, view_y + ime_y},
481  {ime_width, ime_height}}));
482 
483  {
484  auto arguments =
485  std::make_unique<rapidjson::Document>(rapidjson::kObjectType);
486  auto& allocator = arguments->GetAllocator();
487 
488  arguments->AddMember("x", ime_x, allocator);
489  arguments->AddMember("y", ime_y, allocator);
490  arguments->AddMember("width", ime_width, allocator);
491  arguments->AddMember("height", ime_height, allocator);
492 
493  auto message = codec.EncodeMethodCall(
494  {"TextInput.setMarkedTextRect", std::move(arguments)});
495  messenger.SimulateEngineMessage(kChannelName, message->data(),
496  message->size(), reply_handler);
497  }
498 }
499 
500 } // namespace testing
501 } // namespace flutter
flutter::TextInputPlugin::ComposeBeginHook
virtual void ComposeBeginHook()
Definition: text_input_plugin.cc:124
flutter::TextInputPlugin::ComposeChangeHook
virtual void ComposeChangeHook(const std::u16string &text, int cursor_pos)
Definition: text_input_plugin.cc:191
kAffinityDownstream
static constexpr char kAffinityDownstream[]
Definition: text_input_plugin.cc:45
flutter::JsonMessageCodec::GetInstance
static const JsonMessageCodec & GetInstance()
Definition: json_message_codec.cc:17
text_input_plugin.h
kUpdateEditingStateMethod
static constexpr char kUpdateEditingStateMethod[]
Definition: text_input_plugin.cc:27
flutter::TextInputPlugin::ComposeEndHook
virtual void ComposeEndHook()
Definition: text_input_plugin.cc:174
json_method_codec.h
kComposingExtentKey
static constexpr char kComposingExtentKey[]
Definition: text_input_plugin.cc:43
flutter::TextInputPlugin::ComposeCommitHook
virtual void ComposeCommitHook()
Definition: text_input_plugin.cc:139
flutter::Rect
Definition: geometry.h:56
json_message_codec.h
flutter::JsonMethodCodec::GetInstance
static const JsonMethodCodec & GetInstance()
Definition: json_method_codec.cc:36
kEnableDeltaModel
static constexpr char kEnableDeltaModel[]
Definition: text_input_plugin.cc:38
kSelectionIsDirectionalKey
static constexpr char kSelectionIsDirectionalKey[]
Definition: text_input_plugin.cc:48
kSelectionExtentKey
static constexpr char kSelectionExtentKey[]
Definition: text_input_plugin.cc:47
flutter::TextRange
Definition: text_range.h:19
text
std::u16string text
Definition: keyboard_unittests.cc:332
flutter::MethodCall
Definition: method_call.h:18
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:46
flutter
Definition: accessibility_bridge_windows.cc:11
kTextPlainFormat
static constexpr char kTextPlainFormat[]
Definition: platform_handler.cc:38
kSetClientMethod
static constexpr char kSetClientMethod[]
Definition: text_input_plugin.cc:18
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:44
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:84
flutter::MessageCodec::EncodeMessage
std::unique_ptr< std::vector< uint8_t > > EncodeMessage(const T &message) const
Definition: message_codec.h:45
flutter::testing::TEST
TEST(AccessibilityBridgeWindows, GetParent)
Definition: accessibility_bridge_windows_unittests.cc:233
message
Win32Message message
Definition: keyboard_unittests.cc:137
kComposingBaseKey
static constexpr char kComposingBaseKey[]
Definition: text_input_plugin.cc:42
text_input_plugin_delegate.h
flutter::MethodCodec::DecodeMethodCall
std::unique_ptr< MethodCall< T > > DecodeMethodCall(const uint8_t *message, size_t message_size) const
Definition: method_codec.h:32