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

Commit 8325c2c

Browse files
Add Windows exit requests and responses (#40400)
* Communicate exit request * Unit test exit message * Comments * Formatting * Move exit code * Formatting
1 parent d29a736 commit 8325c2c

3 files changed

Lines changed: 158 additions & 1 deletion

File tree

shell/platform/windows/platform_handler.cc

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "flutter/fml/logging.h"
1313
#include "flutter/fml/macros.h"
1414
#include "flutter/fml/platform/win/wstring_conversion.h"
15+
#include "flutter/shell/platform/common/client_wrapper/include/flutter/method_result_functions.h"
1516
#include "flutter/shell/platform/common/json_method_codec.h"
1617
#include "flutter/shell/platform/windows/flutter_windows_view.h"
1718

@@ -20,8 +21,20 @@ static constexpr char kChannelName[] = "flutter/platform";
2021
static constexpr char kGetClipboardDataMethod[] = "Clipboard.getData";
2122
static constexpr char kHasStringsClipboardMethod[] = "Clipboard.hasStrings";
2223
static constexpr char kSetClipboardDataMethod[] = "Clipboard.setData";
24+
static constexpr char kExitApplicationMethod[] = "System.exitApplication";
25+
static constexpr char kRequestAppExitMethod[] = "System.requestAppExit";
2326
static constexpr char kPlaySoundMethod[] = "SystemSound.play";
2427

28+
static constexpr char kExitCodeKey[] = "exitCode";
29+
30+
static constexpr char kExitTypeKey[] = "type";
31+
static constexpr char kExitTypeCancelable[] = "cancelable";
32+
static constexpr char kExitTypeRequired[] = "required";
33+
34+
static constexpr char kExitResponseKey[] = "response";
35+
static constexpr char kExitResponseCancel[] = "cancel";
36+
static constexpr char kExitResponseExit[] = "exit";
37+
2538
static constexpr char kTextPlainFormat[] = "text/plain";
2639
static constexpr char kTextKey[] = "text";
2740
static constexpr char kUnknownClipboardFormatMessage[] =
@@ -340,11 +353,61 @@ void PlatformHandler::SystemSoundPlay(
340353
}
341354
}
342355

356+
void PlatformHandler::SystemExitApplication(
357+
const std::string& exit_type,
358+
int64_t exit_code,
359+
std::unique_ptr<MethodResult<rapidjson::Document>> result) {
360+
rapidjson::Document result_doc;
361+
result_doc.SetObject();
362+
if (exit_type.compare(kExitTypeRequired) == 0) {
363+
QuitApplication(exit_code);
364+
result_doc.GetObjectW().AddMember(kExitResponseKey, kExitResponseExit,
365+
result_doc.GetAllocator());
366+
result->Success(result_doc);
367+
} else {
368+
RequestAppExit(exit_type, exit_code);
369+
result_doc.GetObjectW().AddMember(kExitResponseKey, kExitResponseCancel,
370+
result_doc.GetAllocator());
371+
result->Success(result_doc);
372+
}
373+
}
374+
375+
void PlatformHandler::RequestAppExit(const std::string& exit_type,
376+
int64_t exit_code) {
377+
auto callback = std::make_unique<MethodResultFunctions<rapidjson::Document>>(
378+
[this, exit_code](const rapidjson::Document* response) {
379+
RequestAppExitSuccess(response, exit_code);
380+
},
381+
nullptr, nullptr);
382+
auto args = std::make_unique<rapidjson::Document>();
383+
args->SetObject();
384+
args->GetObjectW().AddMember(kExitTypeKey, exit_type, args->GetAllocator());
385+
channel_->InvokeMethod(kRequestAppExitMethod, std::move(args),
386+
std::move(callback));
387+
}
388+
389+
void PlatformHandler::RequestAppExitSuccess(const rapidjson::Document* result,
390+
int64_t exit_code) {
391+
const std::string& exit_type = result[0][kExitResponseKey].GetString();
392+
if (exit_type.compare(kExitResponseExit) == 0) {
393+
QuitApplication(exit_code);
394+
}
395+
}
396+
397+
void PlatformHandler::QuitApplication(int64_t exit_code) {
398+
PostQuitMessage(exit_code);
399+
}
400+
343401
void PlatformHandler::HandleMethodCall(
344402
const MethodCall<rapidjson::Document>& method_call,
345403
std::unique_ptr<MethodResult<rapidjson::Document>> result) {
346404
const std::string& method = method_call.method_name();
347-
if (method.compare(kGetClipboardDataMethod) == 0) {
405+
if (method.compare(kExitApplicationMethod) == 0) {
406+
const rapidjson::Value& arguments = method_call.arguments()[0];
407+
const std::string& exit_type = arguments[kExitTypeKey].GetString();
408+
int64_t exit_code = arguments[kExitCodeKey].GetInt64();
409+
SystemExitApplication(exit_type, exit_code, std::move(result));
410+
} else if (method.compare(kGetClipboardDataMethod) == 0) {
348411
// Only one string argument is expected.
349412
const rapidjson::Value& format = method_call.arguments()[0];
350413

shell/platform/windows/platform_handler.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,24 @@ class PlatformHandler {
5555
const std::string& sound_type,
5656
std::unique_ptr<MethodResult<rapidjson::Document>> result);
5757

58+
// Handle a request from the framework to exit the application.
59+
virtual void SystemExitApplication(
60+
const std::string& exit_type,
61+
int64_t exit_code,
62+
std::unique_ptr<MethodResult<rapidjson::Document>> result);
63+
64+
// Actually quit the application with the provided exit code.
65+
virtual void QuitApplication(int64_t exit_code);
66+
67+
// Send a request to the framework to test if a cancelable exit request
68+
// should be canceled or honored.
69+
virtual void RequestAppExit(const std::string& exit_type, int64_t exit_code);
70+
71+
// Callback from when the cancelable exit request response request is
72+
// answered by the framework.
73+
virtual void RequestAppExitSuccess(const rapidjson::Document* result,
74+
int64_t exit_code);
75+
5876
// A error type to use for error responses.
5977
static constexpr char kClipboardError[] = "Clipboard error";
6078

shell/platform/windows/platform_handler_unittests.cc

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@ static constexpr char kClipboardSetDataUnknownTypeMessage[] =
4343
"{\"method\":\"Clipboard.setData\",\"args\":{\"madeuptype\":\"hello\"}}";
4444
static constexpr char kSystemSoundTypeAlertMessage[] =
4545
"{\"method\":\"SystemSound.play\",\"args\":\"SystemSoundType.alert\"}";
46+
static constexpr char kSystemExitApplicationRequiredMessage[] =
47+
"{\"method\":\"System.exitApplication\",\"args\":{\"type\":\"required\","
48+
"\"exitCode\":1}}";
49+
static constexpr char kSystemExitApplicationCancelableMessage[] =
50+
"{\"method\":\"System.exitApplication\",\"args\":{\"type\":\"cancelable\","
51+
"\"exitCode\":2}}";
52+
static constexpr char kExitResponseCancelMessage[] =
53+
"[{\"response\":\"cancel\"}]";
54+
static constexpr char kExitResponseExitMessage[] = "[{\"response\":\"exit\"}]";
4655

4756
static constexpr int kAccessDeniedErrorCode = 5;
4857
static constexpr int kErrorSuccess = 0;
@@ -73,6 +82,8 @@ class MockPlatformHandler : public PlatformHandler {
7382
void(const std::string&,
7483
std::unique_ptr<MethodResult<rapidjson::Document>>));
7584

85+
MOCK_METHOD1(QuitApplication, void(int64_t exit_code));
86+
7687
private:
7788
FML_DISALLOW_COPY_AND_ASSIGN(MockPlatformHandler);
7889
};
@@ -470,5 +481,70 @@ TEST_F(PlatformHandlerTest, PlaySystemSound) {
470481
EXPECT_EQ(result, "[null]");
471482
}
472483

484+
TEST_F(PlatformHandlerTest, SystemExitApplicationRequired) {
485+
use_headless_engine();
486+
int exit_code = -1;
487+
488+
TestBinaryMessenger messenger([](const std::string& channel,
489+
const uint8_t* message, size_t size,
490+
BinaryReply reply) {});
491+
MockPlatformHandler platform_handler(&messenger, engine());
492+
493+
ON_CALL(platform_handler, QuitApplication)
494+
.WillByDefault([&exit_code](int ec) { exit_code = ec; });
495+
EXPECT_CALL(platform_handler, QuitApplication).Times(1);
496+
497+
std::string result = SimulatePlatformMessage(
498+
&messenger, kSystemExitApplicationRequiredMessage);
499+
EXPECT_EQ(result, "[{\"response\":\"exit\"}]");
500+
EXPECT_EQ(exit_code, 1);
501+
}
502+
503+
TEST_F(PlatformHandlerTest, SystemExitApplicationCancelableCancel) {
504+
use_headless_engine();
505+
bool called_cancel = false;
506+
507+
TestBinaryMessenger messenger(
508+
[&called_cancel](const std::string& channel, const uint8_t* message,
509+
size_t size, BinaryReply reply) {
510+
reply(reinterpret_cast<const uint8_t*>(kExitResponseCancelMessage),
511+
sizeof(kExitResponseCancelMessage));
512+
called_cancel = true;
513+
});
514+
MockPlatformHandler platform_handler(&messenger, engine());
515+
516+
EXPECT_CALL(platform_handler, QuitApplication).Times(0);
517+
518+
std::string result = SimulatePlatformMessage(
519+
&messenger, kSystemExitApplicationCancelableMessage);
520+
EXPECT_EQ(result, "[{\"response\":\"cancel\"}]");
521+
EXPECT_TRUE(called_cancel);
522+
}
523+
524+
TEST_F(PlatformHandlerTest, SystemExitApplicationCancelableExit) {
525+
use_headless_engine();
526+
bool called_cancel = false;
527+
int exit_code = -1;
528+
529+
TestBinaryMessenger messenger(
530+
[&called_cancel](const std::string& channel, const uint8_t* message,
531+
size_t size, BinaryReply reply) {
532+
reply(reinterpret_cast<const uint8_t*>(kExitResponseExitMessage),
533+
sizeof(kExitResponseExitMessage));
534+
called_cancel = true;
535+
});
536+
MockPlatformHandler platform_handler(&messenger, engine());
537+
538+
ON_CALL(platform_handler, QuitApplication)
539+
.WillByDefault([&exit_code](int ec) { exit_code = ec; });
540+
EXPECT_CALL(platform_handler, QuitApplication).Times(1);
541+
542+
std::string result = SimulatePlatformMessage(
543+
&messenger, kSystemExitApplicationCancelableMessage);
544+
EXPECT_EQ(result, "[{\"response\":\"cancel\"}]");
545+
EXPECT_TRUE(called_cancel);
546+
EXPECT_EQ(exit_code, 2);
547+
}
548+
473549
} // namespace testing
474550
} // namespace flutter

0 commit comments

Comments
 (0)