Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion shell/platform/windows/text_input_plugin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,29 @@ void TextInputPlugin::ComposeCommitHook() {
return;
}
active_model_->CommitComposing();
SendStateUpdate(*active_model_);

// We do not trigger SendStateUpdate here.
//
// Until a WM_IME_ENDCOMPOSING event, the user is still composing from the OS
// point of view. Commit events are always immediately followed by another
// composing event or an end composing event. However, in the brief window
// between the commit event and the following event, the composing region is
// collapsed. Notifying the framework of this intermediate state will trigger
// any framework code designed to execute at the end of composing, such as
// input formatters, which may try to update the text and send a message back
// to the engine with changes.
//
// This is a particular problem with Korean IMEs, which build up one
// character at a time in their composing region until a keypress that makes
// no sense for the in-progress character. At that point, the result
// character is committed and a compose event is immedidately received with
// the new composing region.
//
// In the case where this event is immediately followed by a composing event,
// the state will be sent in ComposeChangeHook.
//
// In the case where this event is immediately followed by an end composing
// event, the state will be sent in ComposeEndHook.
}

void TextInputPlugin::ComposeEndHook() {
Expand Down
56 changes: 56 additions & 0 deletions shell/platform/windows/text_input_plugin_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,61 @@ TEST(TextInputPluginTest, ClearClientResetsComposing) {
EXPECT_TRUE(delegate.ime_was_reset());
}

// Verify that the embedder sends state update messages to the framework during
// IME composing.
TEST(TextInputPluginTest, VerifyComposingSendStateUpdate) {
bool sent_message = false;
TestBinaryMessenger messenger(
[&sent_message](const std::string& channel, const uint8_t* message,
size_t message_size,
BinaryReply reply) { sent_message = true; });
BinaryReply reply_handler = [](const uint8_t* reply, size_t reply_size) {};

EmptyTextInputPluginDelegate delegate;
TextInputPlugin handler(&messenger, &delegate);

auto& codec = JsonMethodCodec::GetInstance();

// Call TextInput.setClient to initialize the TextInputModel.
auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
auto& allocator = arguments->GetAllocator();
arguments->PushBack(42, allocator);
rapidjson::Value config(rapidjson::kObjectType);
config.AddMember("inputAction", "done", allocator);
config.AddMember("inputType", "text", allocator);
arguments->PushBack(config, allocator);
auto message =
codec.EncodeMethodCall({"TextInput.setClient", std::move(arguments)});
messenger.SimulateEngineMessage("flutter/textinput", message->data(),
message->size(), reply_handler);

// ComposeBeginHook should send state update.
sent_message = false;
handler.ComposeBeginHook();
EXPECT_TRUE(sent_message);

// ComposeChangeHook should send state update.
sent_message = false;
handler.ComposeChangeHook(u"4", 1);
EXPECT_TRUE(sent_message);

// ComposeCommitHook should NOT send state update.
//
// Commit messages are always immediately followed by a change message or an
// end message, both of which will send an update. Sending intermediate state
// with a collapsed composing region will trigger the framework to assume
// composing has ended, which is not the case until a WM_IME_ENDCOMPOSING
// event is received in the main event loop, which will trigger a call to
// ComposeEndHook.
sent_message = false;
handler.ComposeCommitHook();
EXPECT_FALSE(sent_message);

// ComposeEndHook should send state update.
sent_message = false;
handler.ComposeEndHook();
EXPECT_TRUE(sent_message);
}

} // namespace testing
} // namespace flutter