Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

[Windows] Delay enabling app lifecycle states until requested #44238

Merged
merged 4 commits into from
Aug 17, 2023
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
32 changes: 32 additions & 0 deletions shell/platform/windows/fixtures/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,38 @@ void exitTestCancel() async {
await exited.future;
}

@pragma('vm:entry-point')
void enableLifecycleTest() async {
final Completer<ByteData?> finished = Completer<ByteData?>();
ui.channelBuffers.setListener('flutter/lifecycle', (ByteData? data, ui.PlatformMessageResponseCallback callback) async {
if (data != null) {
ui.PlatformDispatcher.instance.sendPlatformMessage(
'flutter/unittest',
data,
(ByteData? reply) {
finished.complete();
});
}
});
await finished.future;
}

@pragma('vm:entry-point')
void enableLifecycleToFrom() async {
ui.channelBuffers.setListener('flutter/lifecycle', (ByteData? data, ui.PlatformMessageResponseCallback callback) async {
if (data != null) {
ui.PlatformDispatcher.instance.sendPlatformMessage(
'flutter/unittest',
data,
(ByteData? reply) {});
}
});
final Completer<ByteData?> enabledLifecycle = Completer<ByteData?>();
ui.PlatformDispatcher.instance.sendPlatformMessage('flutter/platform', ByteData.sublistView(utf8.encode('{"method":"System.initializationComplete"}')), (ByteData? data) {
enabledLifecycle.complete(data);
});
}

@pragma('vm:entry-point')
void customEntrypoint() {}

Expand Down
8 changes: 7 additions & 1 deletion shell/platform/windows/flutter_windows_engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -792,8 +792,14 @@ void FlutterWindowsEngine::OnDwmCompositionChanged() {
view_->OnDwmCompositionChanged();
}

// TODO(yaakovschectman): This enables the flutter/lifecycle channel
// once the System.initializationComplete message is received on
// the flutter/system channel. This is a short-term workaround to
// ensure the framework is initialized and ready to accept lifecycle
// messages. This cross-channel dependency should be removed.
// See: https://github.com/flutter/flutter/issues/132514
void FlutterWindowsEngine::OnApplicationLifecycleEnabled() {
lifecycle_manager_->BeginProcessingClose();
lifecycle_manager_->BeginProcessingLifecycle();
}

void FlutterWindowsEngine::OnWindowStateEvent(HWND hwnd,
Expand Down
109 changes: 109 additions & 0 deletions shell/platform/windows/flutter_windows_engine_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,15 @@ class MockWindowsLifecycleManager : public WindowsLifecycleManager {
MOCK_METHOD4(DispatchMessage, void(HWND, UINT, WPARAM, LPARAM));
MOCK_METHOD0(IsLastWindowOfProcess, bool(void));
MOCK_METHOD1(SetLifecycleState, void(AppLifecycleState));

void BeginProcessingLifecycle() override {
WindowsLifecycleManager::BeginProcessingLifecycle();
if (begin_processing_callback) {
begin_processing_callback();
}
}

std::function<void()> begin_processing_callback = nullptr;
};

TEST_F(FlutterWindowsEngineTest, TestExit) {
Expand Down Expand Up @@ -1002,5 +1011,105 @@ TEST_F(FlutterWindowsEngineTest, InnerWindowHidden) {
AppLifecycleState::kInactive);
}

TEST_F(FlutterWindowsEngineTest, EnableLifecycleState) {
FlutterWindowsEngineBuilder builder{GetContext()};
builder.SetDartEntrypoint("enableLifecycleTest");
bool finished = false;

auto window_binding_handler =
std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
MockFlutterWindowsView view(std::move(window_binding_handler));
view.SetEngine(builder.Build());
FlutterWindowsEngine* engine = view.GetEngine();

EngineModifier modifier(engine);
modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
auto handler = std::make_unique<MockWindowsLifecycleManager>(engine);
ON_CALL(*handler, SetLifecycleState)
.WillByDefault([handler_ptr = handler.get()](AppLifecycleState state) {
handler_ptr->WindowsLifecycleManager::SetLifecycleState(state);
});
modifier.SetLifecycleManager(std::move(handler));

auto binary_messenger =
std::make_unique<BinaryMessengerImpl>(engine->messenger());
// Mark the test only as completed on receiving an inactive state message.
binary_messenger->SetMessageHandler(
"flutter/unittest", [&finished](const uint8_t* message,
size_t message_size, BinaryReply reply) {
std::string contents(message, message + message_size);
EXPECT_NE(contents.find("AppLifecycleState.inactive"),
std::string::npos);
finished = true;
char response[] = "";
reply(reinterpret_cast<uint8_t*>(response), 0);
});

engine->Run();

// Test that setting the state before enabling lifecycle does nothing.
HWND hwnd = reinterpret_cast<HWND>(1);
view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
view.OnWindowStateEvent(hwnd, WindowStateEvent::kHide);
EXPECT_FALSE(finished);

// Test that we can set the state afterwards.
engine->OnApplicationLifecycleEnabled();
view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);

while (!finished) {
engine->task_runner()->ProcessTasks();
}
}

TEST_F(FlutterWindowsEngineTest, LifecycleStateToFrom) {
FlutterWindowsEngineBuilder builder{GetContext()};
builder.SetDartEntrypoint("enableLifecycleToFrom");
bool enabled_lifecycle = false;
bool dart_responded = false;

auto window_binding_handler =
std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
MockFlutterWindowsView view(std::move(window_binding_handler));
view.SetEngine(builder.Build());
FlutterWindowsEngine* engine = view.GetEngine();

EngineModifier modifier(engine);
modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
auto handler = std::make_unique<MockWindowsLifecycleManager>(engine);
ON_CALL(*handler, SetLifecycleState)
.WillByDefault([handler_ptr = handler.get()](AppLifecycleState state) {
handler_ptr->WindowsLifecycleManager::SetLifecycleState(state);
});
handler->begin_processing_callback = [&]() { enabled_lifecycle = true; };
modifier.SetLifecycleManager(std::move(handler));

auto binary_messenger =
std::make_unique<BinaryMessengerImpl>(engine->messenger());
binary_messenger->SetMessageHandler(
"flutter/unittest",
[&](const uint8_t* message, size_t message_size, BinaryReply reply) {
std::string contents(message, message + message_size);
EXPECT_NE(contents.find("AppLifecycleState."), std::string::npos);
dart_responded = true;
char response[] = "";
reply(reinterpret_cast<uint8_t*>(response), 0);
});

engine->Run();

while (!enabled_lifecycle) {
engine->task_runner()->ProcessTasks();
}

HWND hwnd = reinterpret_cast<HWND>(1);
view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
view.OnWindowStateEvent(hwnd, WindowStateEvent::kHide);

while (!dart_responded) {
engine->task_runner()->ProcessTasks();
}
}

} // namespace testing
} // namespace flutter
10 changes: 5 additions & 5 deletions shell/platform/windows/windows_lifecycle_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
namespace flutter {

WindowsLifecycleManager::WindowsLifecycleManager(FlutterWindowsEngine* engine)
: engine_(engine), process_close_(false) {}
: engine_(engine) {}

WindowsLifecycleManager::~WindowsLifecycleManager() {}

Expand Down Expand Up @@ -49,7 +49,7 @@ bool WindowsLifecycleManager::WindowProc(HWND hwnd,
// is, we re-dispatch a new WM_CLOSE message. In order to allow the new
// message to reach other delegates, we ignore it here.
case WM_CLOSE: {
if (!process_close_) {
if (!process_lifecycle_) {
return false;
}
auto key = std::make_tuple(hwnd, wpar, lpar);
Expand Down Expand Up @@ -179,8 +179,8 @@ bool WindowsLifecycleManager::IsLastWindowOfProcess() {
return num_windows <= 1;
}

void WindowsLifecycleManager::BeginProcessingClose() {
process_close_ = true;
void WindowsLifecycleManager::BeginProcessingLifecycle() {
process_lifecycle_ = true;
}

// TODO(schectman): Wait until the platform channel is registered to send
Expand All @@ -191,7 +191,7 @@ void WindowsLifecycleManager::SetLifecycleState(AppLifecycleState state) {
return;
}
state_ = state;
if (engine_) {
if (engine_ && process_lifecycle_) {
const char* state_name = AppLifecycleStateToString(state);
engine_->SendPlatformMessage("flutter/lifecycle",
reinterpret_cast<const uint8_t*>(state_name),
Expand Down
7 changes: 4 additions & 3 deletions shell/platform/windows/windows_lifecycle_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ class WindowsLifecycleManager {
// update the application lifecycle.
bool WindowProc(HWND hwnd, UINT msg, WPARAM w, LPARAM l, LRESULT* result);

// Signal to start consuming WM_CLOSE messages.
void BeginProcessingClose();
// Signal to start consuming WM_CLOSE messages and sending lifecycle state
// update messages.
virtual void BeginProcessingLifecycle();

// Update the app lifecycle state in response to a change in window state.
// When the app lifecycle state actually changes, this sends a platform
Expand Down Expand Up @@ -100,7 +101,7 @@ class WindowsLifecycleManager {

std::map<std::tuple<HWND, WPARAM, LPARAM>, int> sent_close_messages_;

bool process_close_;
bool process_lifecycle_ = false;

std::set<HWND> visible_windows_;

Expand Down