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

Commit fea7d0a

Browse files
Only register top level window message listener upon registering service binding (#41733)
Move registering the `WM_CLOSE` message listener in the engine from initialization to in response to a message sent from `ServiceBinding` upon its initialization so that we do not intercept window messages when there is no binding registered. Addresses flutter/flutter#126033. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides]. - [x] I listed at least one issue that this PR fixes in the description above. - [ ] I added new tests to check the change I am making or feature I am adding, or Hixie said the PR is test-exempt. See [testing the engine] for instructions on writing and running engine tests. - [x] I updated/added relevant documentation (doc comments with `///`). - [ ] I signed the [CLA]. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style [testing the engine]: https://github.com/flutter/flutter/wiki/Testing-the-engine [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat --------- Co-authored-by: Loïc Sharma <737941+loic-sharma@users.noreply.github.com>
1 parent 4db1fa4 commit fea7d0a

File tree

8 files changed

+82
-1
lines changed

8 files changed

+82
-1
lines changed

shell/platform/darwin/macos/framework/Source/FlutterEngine.mm

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,10 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
10311031
result(@{@"value" : @([self clipboardHasStrings])});
10321032
} else if ([call.method isEqualToString:@"System.exitApplication"]) {
10331033
[[self terminationHandler] handleRequestAppExitMethodCall:call.arguments result:result];
1034+
} else if ([call.method isEqualToString:@"System.initializationComplete"]) {
1035+
// TODO(gspencergoog): Handle this message to enable exit message listening.
1036+
// https://github.com/flutter/flutter/issues/126033
1037+
result(nil);
10341038
} else {
10351039
result(FlutterMethodNotImplemented);
10361040
}

shell/platform/linux/fl_platform_plugin.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ static constexpr char kSetClipboardDataMethod[] = "Clipboard.setData";
2020
static constexpr char kClipboardHasStringsMethod[] = "Clipboard.hasStrings";
2121
static constexpr char kExitApplicationMethod[] = "System.exitApplication";
2222
static constexpr char kRequestAppExitMethod[] = "System.requestAppExit";
23+
static constexpr char kInitializationCompleteMethod[] =
24+
"System.initializationComplete";
2325
static constexpr char kPlaySoundMethod[] = "SystemSound.play";
2426
static constexpr char kSystemNavigatorPopMethod[] = "SystemNavigator.pop";
2527
static constexpr char kTextKey[] = "text";
@@ -335,6 +337,10 @@ static void method_call_cb(FlMethodChannel* channel,
335337
response = system_sound_play(self, args);
336338
} else if (strcmp(method, kSystemNavigatorPopMethod) == 0) {
337339
response = system_navigator_pop(self);
340+
} else if (strcmp(method, kInitializationCompleteMethod) == 0) {
341+
// TODO(gspencergoog): Handle this message to enable exit message listening.
342+
// https://github.com/flutter/flutter/issues/126033
343+
response = FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
338344
} else {
339345
response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
340346
}

shell/platform/windows/flutter_windows_engine.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,4 +778,8 @@ void FlutterWindowsEngine::OnDwmCompositionChanged() {
778778
view_->OnDwmCompositionChanged();
779779
}
780780

781+
void FlutterWindowsEngine::OnApplicationLifecycleEnabled() {
782+
lifecycle_manager_->BeginProcessingClose();
783+
}
784+
781785
} // namespace flutter

shell/platform/windows/flutter_windows_engine.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,10 @@ class FlutterWindowsEngine {
269269
// Called when a WM_DWMCOMPOSITIONCHANGED message is received.
270270
void OnDwmCompositionChanged();
271271

272+
// Called in response to the framework registering a ServiceBindings.
273+
// Registers the top level handler for the WM_CLOSE window message.
274+
void OnApplicationLifecycleEnabled();
275+
272276
protected:
273277
// Creates the keyboard key handler.
274278
//

shell/platform/windows/flutter_windows_engine_unittests.cc

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,7 @@ TEST_F(FlutterWindowsEngineTest, TestExit) {
657657
ON_CALL(*handler, IsLastWindowOfProcess).WillByDefault([]() { return true; });
658658
EXPECT_CALL(*handler, Quit).Times(1);
659659
modifier.SetLifecycleManager(std::move(handler));
660+
engine->OnApplicationLifecycleEnabled();
660661

661662
engine->Run();
662663

@@ -693,6 +694,7 @@ TEST_F(FlutterWindowsEngineTest, TestExitCancel) {
693694
ON_CALL(*handler, IsLastWindowOfProcess).WillByDefault([]() { return true; });
694695
EXPECT_CALL(*handler, Quit).Times(0);
695696
modifier.SetLifecycleManager(std::move(handler));
697+
engine->OnApplicationLifecycleEnabled();
696698

697699
auto binary_messenger =
698700
std::make_unique<BinaryMessengerImpl>(engine->messenger());
@@ -753,6 +755,7 @@ TEST_F(FlutterWindowsEngineTest, TestExitSecondCloseMessage) {
753755
hwnd, msg, wparam, lparam);
754756
});
755757
modifier.SetLifecycleManager(std::move(handler));
758+
engine->OnApplicationLifecycleEnabled();
756759

757760
engine->Run();
758761

@@ -803,6 +806,7 @@ TEST_F(FlutterWindowsEngineTest, TestExitCloseMultiWindow) {
803806
// Quit should not be called when there is more than one window.
804807
EXPECT_CALL(*handler, Quit).Times(0);
805808
modifier.SetLifecycleManager(std::move(handler));
809+
engine->OnApplicationLifecycleEnabled();
806810

807811
engine->Run();
808812

@@ -814,5 +818,47 @@ TEST_F(FlutterWindowsEngineTest, TestExitCloseMultiWindow) {
814818
}
815819
}
816820

821+
TEST_F(FlutterWindowsEngineTest, LifecycleManagerDisabledByDefault) {
822+
FlutterWindowsEngineBuilder builder{GetContext()};
823+
824+
auto window_binding_handler =
825+
std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
826+
MockFlutterWindowsView view(std::move(window_binding_handler));
827+
view.SetEngine(builder.Build());
828+
FlutterWindowsEngine* engine = view.GetEngine();
829+
830+
EngineModifier modifier(engine);
831+
modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
832+
auto handler = std::make_unique<MockWindowsLifecycleManager>(engine);
833+
EXPECT_CALL(*handler, IsLastWindowOfProcess).Times(0);
834+
modifier.SetLifecycleManager(std::move(handler));
835+
836+
engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
837+
0);
838+
}
839+
840+
TEST_F(FlutterWindowsEngineTest, EnableApplicationLifecycle) {
841+
FlutterWindowsEngineBuilder builder{GetContext()};
842+
843+
auto window_binding_handler =
844+
std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
845+
MockFlutterWindowsView view(std::move(window_binding_handler));
846+
view.SetEngine(builder.Build());
847+
FlutterWindowsEngine* engine = view.GetEngine();
848+
849+
EngineModifier modifier(engine);
850+
modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
851+
auto handler = std::make_unique<MockWindowsLifecycleManager>(engine);
852+
ON_CALL(*handler, IsLastWindowOfProcess).WillByDefault([]() {
853+
return false;
854+
});
855+
EXPECT_CALL(*handler, IsLastWindowOfProcess).Times(1);
856+
modifier.SetLifecycleManager(std::move(handler));
857+
engine->OnApplicationLifecycleEnabled();
858+
859+
engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
860+
0);
861+
}
862+
817863
} // namespace testing
818864
} // namespace flutter

shell/platform/windows/platform_handler.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ static constexpr char kHasStringsClipboardMethod[] = "Clipboard.hasStrings";
2323
static constexpr char kSetClipboardDataMethod[] = "Clipboard.setData";
2424
static constexpr char kExitApplicationMethod[] = "System.exitApplication";
2525
static constexpr char kRequestAppExitMethod[] = "System.requestAppExit";
26+
static constexpr char kInitializationCompleteMethod[] =
27+
"System.initializationComplete";
2628
static constexpr char kPlaySoundMethod[] = "SystemSound.play";
2729

2830
static constexpr char kExitCodeKey[] = "exitCode";
@@ -495,6 +497,9 @@ void PlatformHandler::HandleMethodCall(
495497
const rapidjson::Value& sound_type = method_call.arguments()[0];
496498

497499
SystemSoundPlay(sound_type.GetString(), std::move(result));
500+
} else if (method.compare(kInitializationCompleteMethod) == 0) {
501+
engine_->OnApplicationLifecycleEnabled();
502+
result->Success();
498503
} else {
499504
result->NotImplemented();
500505
}

shell/platform/windows/windows_lifecycle_manager.cc

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
namespace flutter {
1515

1616
WindowsLifecycleManager::WindowsLifecycleManager(FlutterWindowsEngine* engine)
17-
: engine_(engine) {}
17+
: engine_(engine), process_close_(false) {}
1818

1919
WindowsLifecycleManager::~WindowsLifecycleManager() {}
2020

@@ -49,6 +49,9 @@ bool WindowsLifecycleManager::WindowProc(HWND hwnd,
4949
// is, we re-dispatch a new WM_CLOSE message. In order to allow the new
5050
// message to reach other delegates, we ignore it here.
5151
case WM_CLOSE: {
52+
if (!process_close_) {
53+
return false;
54+
}
5255
auto key = std::make_tuple(hwnd, wpar, lpar);
5356
auto itr = sent_close_messages_.find(key);
5457
if (itr != sent_close_messages_.end()) {
@@ -156,4 +159,8 @@ bool WindowsLifecycleManager::IsLastWindowOfProcess() {
156159
return num_windows <= 1;
157160
}
158161

162+
void WindowsLifecycleManager::BeginProcessingClose() {
163+
process_close_ = true;
164+
}
165+
159166
} // namespace flutter

shell/platform/windows/windows_lifecycle_manager.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ class WindowsLifecycleManager {
3737
// Intercept top level window messages, only paying attention to WM_CLOSE.
3838
bool WindowProc(HWND hwnd, UINT msg, WPARAM w, LPARAM l, LRESULT* result);
3939

40+
// Signal to start consuming WM_CLOSE messages.
41+
void BeginProcessingClose();
42+
4043
protected:
4144
// Check the number of top-level windows associated with this process, and
4245
// return true only if there are 1 or fewer.
@@ -51,6 +54,8 @@ class WindowsLifecycleManager {
5154
FlutterWindowsEngine* engine_;
5255

5356
std::map<std::tuple<HWND, WPARAM, LPARAM>, int> sent_close_messages_;
57+
58+
bool process_close_;
5459
};
5560

5661
} // namespace flutter

0 commit comments

Comments
 (0)