diff --git a/shell/platform/windows/platform_handler.cc b/shell/platform/windows/platform_handler.cc index 4c54b3d4cf09b..a33710becbf9d 100644 --- a/shell/platform/windows/platform_handler.cc +++ b/shell/platform/windows/platform_handler.cc @@ -12,6 +12,7 @@ #include "flutter/fml/logging.h" #include "flutter/fml/macros.h" #include "flutter/fml/platform/win/wstring_conversion.h" +#include "flutter/shell/platform/common/client_wrapper/include/flutter/method_result_functions.h" #include "flutter/shell/platform/common/json_method_codec.h" #include "flutter/shell/platform/windows/flutter_windows_view.h" @@ -20,8 +21,20 @@ static constexpr char kChannelName[] = "flutter/platform"; static constexpr char kGetClipboardDataMethod[] = "Clipboard.getData"; static constexpr char kHasStringsClipboardMethod[] = "Clipboard.hasStrings"; static constexpr char kSetClipboardDataMethod[] = "Clipboard.setData"; +static constexpr char kExitApplicationMethod[] = "System.exitApplication"; +static constexpr char kRequestAppExitMethod[] = "System.requestAppExit"; static constexpr char kPlaySoundMethod[] = "SystemSound.play"; +static constexpr char kExitCodeKey[] = "exitCode"; + +static constexpr char kExitTypeKey[] = "type"; +static constexpr char kExitTypeCancelable[] = "cancelable"; +static constexpr char kExitTypeRequired[] = "required"; + +static constexpr char kExitResponseKey[] = "response"; +static constexpr char kExitResponseCancel[] = "cancel"; +static constexpr char kExitResponseExit[] = "exit"; + static constexpr char kTextPlainFormat[] = "text/plain"; static constexpr char kTextKey[] = "text"; static constexpr char kUnknownClipboardFormatMessage[] = @@ -340,11 +353,61 @@ void PlatformHandler::SystemSoundPlay( } } +void PlatformHandler::SystemExitApplication( + const std::string& exit_type, + int64_t exit_code, + std::unique_ptr> result) { + rapidjson::Document result_doc; + result_doc.SetObject(); + if (exit_type.compare(kExitTypeRequired) == 0) { + QuitApplication(exit_code); + result_doc.GetObjectW().AddMember(kExitResponseKey, kExitResponseExit, + result_doc.GetAllocator()); + result->Success(result_doc); + } else { + RequestAppExit(exit_type, exit_code); + result_doc.GetObjectW().AddMember(kExitResponseKey, kExitResponseCancel, + result_doc.GetAllocator()); + result->Success(result_doc); + } +} + +void PlatformHandler::RequestAppExit(const std::string& exit_type, + int64_t exit_code) { + auto callback = std::make_unique>( + [this, exit_code](const rapidjson::Document* response) { + RequestAppExitSuccess(response, exit_code); + }, + nullptr, nullptr); + auto args = std::make_unique(); + args->SetObject(); + args->GetObjectW().AddMember(kExitTypeKey, exit_type, args->GetAllocator()); + channel_->InvokeMethod(kRequestAppExitMethod, std::move(args), + std::move(callback)); +} + +void PlatformHandler::RequestAppExitSuccess(const rapidjson::Document* result, + int64_t exit_code) { + const std::string& exit_type = result[0][kExitResponseKey].GetString(); + if (exit_type.compare(kExitResponseExit) == 0) { + QuitApplication(exit_code); + } +} + +void PlatformHandler::QuitApplication(int64_t exit_code) { + PostQuitMessage(exit_code); +} + void PlatformHandler::HandleMethodCall( const MethodCall& method_call, std::unique_ptr> result) { const std::string& method = method_call.method_name(); - if (method.compare(kGetClipboardDataMethod) == 0) { + if (method.compare(kExitApplicationMethod) == 0) { + const rapidjson::Value& arguments = method_call.arguments()[0]; + const std::string& exit_type = arguments[kExitTypeKey].GetString(); + int64_t exit_code = arguments[kExitCodeKey].GetInt64(); + SystemExitApplication(exit_type, exit_code, std::move(result)); + } else if (method.compare(kGetClipboardDataMethod) == 0) { // Only one string argument is expected. const rapidjson::Value& format = method_call.arguments()[0]; diff --git a/shell/platform/windows/platform_handler.h b/shell/platform/windows/platform_handler.h index 35f1cef14ff79..096378db4f414 100644 --- a/shell/platform/windows/platform_handler.h +++ b/shell/platform/windows/platform_handler.h @@ -55,6 +55,24 @@ class PlatformHandler { const std::string& sound_type, std::unique_ptr> result); + // Handle a request from the framework to exit the application. + virtual void SystemExitApplication( + const std::string& exit_type, + int64_t exit_code, + std::unique_ptr> result); + + // Actually quit the application with the provided exit code. + virtual void QuitApplication(int64_t exit_code); + + // Send a request to the framework to test if a cancelable exit request + // should be canceled or honored. + virtual void RequestAppExit(const std::string& exit_type, int64_t exit_code); + + // Callback from when the cancelable exit request response request is + // answered by the framework. + virtual void RequestAppExitSuccess(const rapidjson::Document* result, + int64_t exit_code); + // A error type to use for error responses. static constexpr char kClipboardError[] = "Clipboard error"; diff --git a/shell/platform/windows/platform_handler_unittests.cc b/shell/platform/windows/platform_handler_unittests.cc index 71f383ae738e7..85d4f5a9a9713 100644 --- a/shell/platform/windows/platform_handler_unittests.cc +++ b/shell/platform/windows/platform_handler_unittests.cc @@ -43,6 +43,15 @@ static constexpr char kClipboardSetDataUnknownTypeMessage[] = "{\"method\":\"Clipboard.setData\",\"args\":{\"madeuptype\":\"hello\"}}"; static constexpr char kSystemSoundTypeAlertMessage[] = "{\"method\":\"SystemSound.play\",\"args\":\"SystemSoundType.alert\"}"; +static constexpr char kSystemExitApplicationRequiredMessage[] = + "{\"method\":\"System.exitApplication\",\"args\":{\"type\":\"required\"," + "\"exitCode\":1}}"; +static constexpr char kSystemExitApplicationCancelableMessage[] = + "{\"method\":\"System.exitApplication\",\"args\":{\"type\":\"cancelable\"," + "\"exitCode\":2}}"; +static constexpr char kExitResponseCancelMessage[] = + "[{\"response\":\"cancel\"}]"; +static constexpr char kExitResponseExitMessage[] = "[{\"response\":\"exit\"}]"; static constexpr int kAccessDeniedErrorCode = 5; static constexpr int kErrorSuccess = 0; @@ -73,6 +82,8 @@ class MockPlatformHandler : public PlatformHandler { void(const std::string&, std::unique_ptr>)); + MOCK_METHOD1(QuitApplication, void(int64_t exit_code)); + private: FML_DISALLOW_COPY_AND_ASSIGN(MockPlatformHandler); }; @@ -470,5 +481,70 @@ TEST_F(PlatformHandlerTest, PlaySystemSound) { EXPECT_EQ(result, "[null]"); } +TEST_F(PlatformHandlerTest, SystemExitApplicationRequired) { + use_headless_engine(); + int exit_code = -1; + + TestBinaryMessenger messenger([](const std::string& channel, + const uint8_t* message, size_t size, + BinaryReply reply) {}); + MockPlatformHandler platform_handler(&messenger, engine()); + + ON_CALL(platform_handler, QuitApplication) + .WillByDefault([&exit_code](int ec) { exit_code = ec; }); + EXPECT_CALL(platform_handler, QuitApplication).Times(1); + + std::string result = SimulatePlatformMessage( + &messenger, kSystemExitApplicationRequiredMessage); + EXPECT_EQ(result, "[{\"response\":\"exit\"}]"); + EXPECT_EQ(exit_code, 1); +} + +TEST_F(PlatformHandlerTest, SystemExitApplicationCancelableCancel) { + use_headless_engine(); + bool called_cancel = false; + + TestBinaryMessenger messenger( + [&called_cancel](const std::string& channel, const uint8_t* message, + size_t size, BinaryReply reply) { + reply(reinterpret_cast(kExitResponseCancelMessage), + sizeof(kExitResponseCancelMessage)); + called_cancel = true; + }); + MockPlatformHandler platform_handler(&messenger, engine()); + + EXPECT_CALL(platform_handler, QuitApplication).Times(0); + + std::string result = SimulatePlatformMessage( + &messenger, kSystemExitApplicationCancelableMessage); + EXPECT_EQ(result, "[{\"response\":\"cancel\"}]"); + EXPECT_TRUE(called_cancel); +} + +TEST_F(PlatformHandlerTest, SystemExitApplicationCancelableExit) { + use_headless_engine(); + bool called_cancel = false; + int exit_code = -1; + + TestBinaryMessenger messenger( + [&called_cancel](const std::string& channel, const uint8_t* message, + size_t size, BinaryReply reply) { + reply(reinterpret_cast(kExitResponseExitMessage), + sizeof(kExitResponseExitMessage)); + called_cancel = true; + }); + MockPlatformHandler platform_handler(&messenger, engine()); + + ON_CALL(platform_handler, QuitApplication) + .WillByDefault([&exit_code](int ec) { exit_code = ec; }); + EXPECT_CALL(platform_handler, QuitApplication).Times(1); + + std::string result = SimulatePlatformMessage( + &messenger, kSystemExitApplicationCancelableMessage); + EXPECT_EQ(result, "[{\"response\":\"cancel\"}]"); + EXPECT_TRUE(called_cancel); + EXPECT_EQ(exit_code, 2); +} + } // namespace testing } // namespace flutter