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
65 changes: 64 additions & 1 deletion shell/platform/windows/platform_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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[] =
Expand Down Expand Up @@ -340,11 +353,61 @@ void PlatformHandler::SystemSoundPlay(
}
}

void PlatformHandler::SystemExitApplication(
const std::string& exit_type,
int64_t exit_code,
std::unique_ptr<MethodResult<rapidjson::Document>> 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<MethodResultFunctions<rapidjson::Document>>(
[this, exit_code](const rapidjson::Document* response) {
RequestAppExitSuccess(response, exit_code);
},
nullptr, nullptr);
auto args = std::make_unique<rapidjson::Document>();
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<rapidjson::Document>& method_call,
std::unique_ptr<MethodResult<rapidjson::Document>> 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];

Expand Down
18 changes: 18 additions & 0 deletions shell/platform/windows/platform_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,24 @@ class PlatformHandler {
const std::string& sound_type,
std::unique_ptr<MethodResult<rapidjson::Document>> 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<MethodResult<rapidjson::Document>> 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";

Expand Down
76 changes: 76 additions & 0 deletions shell/platform/windows/platform_handler_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -73,6 +82,8 @@ class MockPlatformHandler : public PlatformHandler {
void(const std::string&,
std::unique_ptr<MethodResult<rapidjson::Document>>));

MOCK_METHOD1(QuitApplication, void(int64_t exit_code));

private:
FML_DISALLOW_COPY_AND_ASSIGN(MockPlatformHandler);
};
Expand Down Expand Up @@ -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<const uint8_t*>(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<const uint8_t*>(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