mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
499 lines
18 KiB
C++
499 lines
18 KiB
C++
// Copyright 2013 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// This file is a historical legacy, predating the proc table API. It has been
|
|
// updated to continue to work with the proc table, but new tests should not
|
|
// rely on replacements set up here, but instead use test-local replacements
|
|
// for any functions relevant to that test.
|
|
//
|
|
// Over time existing tests should be migrated and this file should be removed.
|
|
|
|
#include <cstring>
|
|
|
|
#include "flutter/shell/platform/embedder/embedder.h"
|
|
#include "flutter/shell/platform/linux/fl_method_codec_private.h"
|
|
#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h"
|
|
#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_response.h"
|
|
#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
const int32_t kFlutterSemanticsCustomActionIdBatchEnd = -1;
|
|
|
|
struct _FlutterEngine {
|
|
bool running;
|
|
FlutterPlatformMessageCallback platform_message_callback;
|
|
FlutterTaskRunnerPostTaskCallback platform_post_task_callback;
|
|
void* user_data;
|
|
|
|
_FlutterEngine(FlutterPlatformMessageCallback platform_message_callback,
|
|
FlutterTaskRunnerPostTaskCallback platform_post_task_callback,
|
|
void* user_data)
|
|
: running(false),
|
|
platform_message_callback(platform_message_callback),
|
|
platform_post_task_callback(platform_post_task_callback),
|
|
user_data(user_data) {}
|
|
};
|
|
|
|
struct _FlutterPlatformMessageResponseHandle {
|
|
FlutterDataCallback data_callback;
|
|
void* user_data;
|
|
std::string channel;
|
|
bool released;
|
|
|
|
// Constructor for a response handle generated by the engine.
|
|
explicit _FlutterPlatformMessageResponseHandle(std::string channel)
|
|
: data_callback(nullptr),
|
|
user_data(nullptr),
|
|
channel(channel),
|
|
released(false) {}
|
|
|
|
// Constructor for a response handle generated by the shell.
|
|
_FlutterPlatformMessageResponseHandle(FlutterDataCallback data_callback,
|
|
void* user_data)
|
|
: data_callback(data_callback), user_data(user_data), released(false) {}
|
|
};
|
|
|
|
struct _FlutterTaskRunner {
|
|
uint64_t task;
|
|
std::string channel;
|
|
const FlutterPlatformMessageResponseHandle* response_handle;
|
|
uint8_t* message;
|
|
size_t message_size;
|
|
|
|
_FlutterTaskRunner(
|
|
uint64_t task,
|
|
const std::string& channel,
|
|
const FlutterPlatformMessageResponseHandle* response_handle,
|
|
const uint8_t* message,
|
|
size_t message_size)
|
|
: task(task),
|
|
channel(channel),
|
|
response_handle(response_handle),
|
|
message_size(message_size) {
|
|
if (message_size > 0) {
|
|
this->message = static_cast<uint8_t*>(malloc(message_size));
|
|
memcpy(this->message, message, message_size);
|
|
} else {
|
|
this->message = nullptr;
|
|
}
|
|
}
|
|
~_FlutterTaskRunner() {
|
|
if (response_handle != nullptr) {
|
|
EXPECT_TRUE(response_handle->released);
|
|
delete response_handle;
|
|
}
|
|
free(message);
|
|
}
|
|
};
|
|
|
|
namespace {
|
|
|
|
// Send a response from the engine.
|
|
static void send_response(
|
|
FLUTTER_API_SYMBOL(FlutterEngine) engine,
|
|
const std::string& channel,
|
|
const FlutterPlatformMessageResponseHandle* response_handle,
|
|
const uint8_t* message,
|
|
size_t message_size) {
|
|
if (response_handle == nullptr) {
|
|
return;
|
|
}
|
|
|
|
FlutterTask task;
|
|
task.runner = new _FlutterTaskRunner(1234, channel, response_handle, message,
|
|
message_size);
|
|
task.task = task.runner->task;
|
|
engine->platform_post_task_callback(task, 0, engine->user_data);
|
|
}
|
|
|
|
// Send a message from the engine.
|
|
static void send_message(FLUTTER_API_SYMBOL(FlutterEngine) engine,
|
|
const std::string& channel,
|
|
const uint8_t* message,
|
|
size_t message_size) {
|
|
FlutterTask task;
|
|
task.runner =
|
|
new _FlutterTaskRunner(1234, channel, nullptr, message, message_size);
|
|
task.task = task.runner->task;
|
|
engine->platform_post_task_callback(task, 0, engine->user_data);
|
|
}
|
|
|
|
static void invoke_method(FLUTTER_API_SYMBOL(FlutterEngine) engine,
|
|
const std::string& channel,
|
|
const gchar* name,
|
|
FlValue* args) {
|
|
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
|
|
g_autoptr(GError) error = nullptr;
|
|
g_autoptr(GBytes) message = fl_method_codec_encode_method_call(
|
|
FL_METHOD_CODEC(codec), name, args, &error);
|
|
EXPECT_NE(message, nullptr);
|
|
EXPECT_EQ(error, nullptr);
|
|
|
|
FlutterTask task;
|
|
task.runner = new _FlutterTaskRunner(
|
|
1234, channel, nullptr,
|
|
static_cast<const uint8_t*>(g_bytes_get_data(message, nullptr)),
|
|
g_bytes_get_size(message));
|
|
task.task = task.runner->task;
|
|
engine->platform_post_task_callback(task, 0, engine->user_data);
|
|
}
|
|
|
|
FlutterEngineResult FlutterEngineCreateAOTData(
|
|
const FlutterEngineAOTDataSource* source,
|
|
FlutterEngineAOTData* data_out) {
|
|
*data_out = nullptr;
|
|
return kSuccess;
|
|
}
|
|
|
|
FlutterEngineResult FlutterEngineCollectAOTData(FlutterEngineAOTData data) {
|
|
return kSuccess;
|
|
}
|
|
|
|
FlutterEngineResult FlutterEngineInitialize(size_t version,
|
|
const FlutterRendererConfig* config,
|
|
const FlutterProjectArgs* args,
|
|
void* user_data,
|
|
FLUTTER_API_SYMBOL(FlutterEngine) *
|
|
engine_out) {
|
|
EXPECT_NE(config, nullptr);
|
|
|
|
EXPECT_NE(args, nullptr);
|
|
EXPECT_NE(args->platform_message_callback, nullptr);
|
|
EXPECT_NE(args->custom_task_runners, nullptr);
|
|
EXPECT_NE(args->custom_task_runners->platform_task_runner, nullptr);
|
|
EXPECT_NE(args->custom_task_runners->platform_task_runner->post_task_callback,
|
|
nullptr);
|
|
|
|
EXPECT_NE(user_data, nullptr);
|
|
|
|
EXPECT_EQ(config->type, kOpenGL);
|
|
|
|
*engine_out = new _FlutterEngine(
|
|
args->platform_message_callback,
|
|
args->custom_task_runners->platform_task_runner->post_task_callback,
|
|
user_data);
|
|
return kSuccess;
|
|
}
|
|
|
|
FlutterEngineResult FlutterEngineRunInitialized(
|
|
FLUTTER_API_SYMBOL(FlutterEngine) engine) {
|
|
engine->running = true;
|
|
return kSuccess;
|
|
}
|
|
|
|
FlutterEngineResult FlutterEngineRun(size_t version,
|
|
const FlutterRendererConfig* config,
|
|
const FlutterProjectArgs* args,
|
|
void* user_data,
|
|
FLUTTER_API_SYMBOL(FlutterEngine) *
|
|
engine_out) {
|
|
EXPECT_NE(config, nullptr);
|
|
EXPECT_NE(args, nullptr);
|
|
EXPECT_NE(user_data, nullptr);
|
|
EXPECT_NE(engine_out, nullptr);
|
|
|
|
FlutterEngineResult result =
|
|
FlutterEngineInitialize(version, config, args, user_data, engine_out);
|
|
if (result != kSuccess) {
|
|
return result;
|
|
}
|
|
return FlutterEngineRunInitialized(*engine_out);
|
|
}
|
|
|
|
FlutterEngineResult FlutterEngineShutdown(FLUTTER_API_SYMBOL(FlutterEngine)
|
|
engine) {
|
|
delete engine;
|
|
return kSuccess;
|
|
}
|
|
|
|
FlutterEngineResult FlutterEngineDeinitialize(FLUTTER_API_SYMBOL(FlutterEngine)
|
|
engine) {
|
|
return kSuccess;
|
|
}
|
|
|
|
FlutterEngineResult FlutterEngineSendWindowMetricsEvent(
|
|
FLUTTER_API_SYMBOL(FlutterEngine) engine,
|
|
const FlutterWindowMetricsEvent* event) {
|
|
EXPECT_TRUE(engine->running);
|
|
return kSuccess;
|
|
}
|
|
|
|
FlutterEngineResult FlutterEngineSendPointerEvent(
|
|
FLUTTER_API_SYMBOL(FlutterEngine) engine,
|
|
const FlutterPointerEvent* events,
|
|
size_t events_count) {
|
|
return kSuccess;
|
|
}
|
|
|
|
FLUTTER_EXPORT
|
|
FlutterEngineResult FlutterEngineSendPlatformMessage(
|
|
FLUTTER_API_SYMBOL(FlutterEngine) engine,
|
|
const FlutterPlatformMessage* message) {
|
|
EXPECT_TRUE(engine->running);
|
|
|
|
if (strcmp(message->channel, "test/echo") == 0) {
|
|
// Responds with the same message received.
|
|
send_response(engine, message->channel, message->response_handle,
|
|
message->message, message->message_size);
|
|
} else if (strcmp(message->channel, "test/send-message") == 0) {
|
|
// Triggers the engine to send a message.
|
|
send_response(engine, message->channel, message->response_handle, nullptr,
|
|
0);
|
|
send_message(engine, "test/messages", message->message,
|
|
message->message_size);
|
|
} else if (strcmp(message->channel, "test/standard-method") == 0) {
|
|
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
|
|
g_autoptr(GBytes) m = g_bytes_new(message->message, message->message_size);
|
|
g_autofree gchar* name = nullptr;
|
|
g_autoptr(FlValue) args = nullptr;
|
|
g_autoptr(GError) error = nullptr;
|
|
EXPECT_TRUE(fl_method_codec_decode_method_call(FL_METHOD_CODEC(codec), m,
|
|
&name, &args, &error));
|
|
EXPECT_EQ(error, nullptr);
|
|
|
|
g_autoptr(GBytes) response = nullptr;
|
|
if (strcmp(name, "Echo") == 0) {
|
|
// Returns args as a success result.
|
|
response = fl_method_codec_encode_success_envelope(FL_METHOD_CODEC(codec),
|
|
args, &error);
|
|
EXPECT_EQ(error, nullptr);
|
|
} else if (strcmp(name, "Error") == 0) {
|
|
// Returns an error result.
|
|
const gchar* code = nullptr;
|
|
const gchar* message = nullptr;
|
|
FlValue* details = nullptr;
|
|
if (fl_value_get_length(args) >= 2) {
|
|
FlValue* code_value = fl_value_get_list_value(args, 0);
|
|
EXPECT_EQ(fl_value_get_type(code_value), FL_VALUE_TYPE_STRING);
|
|
code = fl_value_get_string(code_value);
|
|
FlValue* message_value = fl_value_get_list_value(args, 1);
|
|
message = fl_value_get_type(message_value) == FL_VALUE_TYPE_STRING
|
|
? fl_value_get_string(message_value)
|
|
: nullptr;
|
|
}
|
|
if (fl_value_get_length(args) >= 3) {
|
|
details = fl_value_get_list_value(args, 2);
|
|
}
|
|
response = fl_method_codec_encode_error_envelope(
|
|
FL_METHOD_CODEC(codec), code, message, details, &error);
|
|
EXPECT_EQ(error, nullptr);
|
|
} else if (strcmp(name, "InvokeMethod") == 0) {
|
|
// Gets the engine to call the shell.
|
|
if (fl_value_get_length(args) == 3) {
|
|
FlValue* channel_value = fl_value_get_list_value(args, 0);
|
|
EXPECT_EQ(fl_value_get_type(channel_value), FL_VALUE_TYPE_STRING);
|
|
const gchar* channel = fl_value_get_string(channel_value);
|
|
FlValue* name_value = fl_value_get_list_value(args, 1);
|
|
EXPECT_EQ(fl_value_get_type(name_value), FL_VALUE_TYPE_STRING);
|
|
const gchar* name = fl_value_get_string(name_value);
|
|
FlValue* method_args = fl_value_get_list_value(args, 2);
|
|
invoke_method(engine, channel, name, method_args);
|
|
}
|
|
response = fl_method_codec_encode_success_envelope(FL_METHOD_CODEC(codec),
|
|
nullptr, &error);
|
|
EXPECT_EQ(error, nullptr);
|
|
} else {
|
|
// Returns "not implemented".
|
|
response = g_bytes_new(nullptr, 0);
|
|
}
|
|
|
|
send_response(
|
|
engine, message->channel, message->response_handle,
|
|
static_cast<const uint8_t*>(g_bytes_get_data(response, nullptr)),
|
|
g_bytes_get_size(response));
|
|
} else if (strcmp(message->channel, "test/nullptr-response") == 0) {
|
|
// Sends a null response.
|
|
send_response(engine, message->channel, message->response_handle, nullptr,
|
|
0);
|
|
} else if (strcmp(message->channel, "test/standard-event") == 0) {
|
|
// Send a message so the shell can check the events sent.
|
|
send_message(engine, "test/events", message->message,
|
|
message->message_size);
|
|
} else if (strcmp(message->channel, "test/failure") == 0) {
|
|
// Generates an internal error.
|
|
return kInternalInconsistency;
|
|
} else if (strcmp(message->channel, "test/key-event-handled") == 0 ||
|
|
strcmp(message->channel, "test/key-event-not-handled") == 0) {
|
|
bool value = strcmp(message->channel, "test/key-event-handled") == 0;
|
|
g_autoptr(FlJsonMessageCodec) codec = fl_json_message_codec_new();
|
|
g_autoptr(FlValue) handledValue = fl_value_new_map();
|
|
fl_value_set_string_take(handledValue, "handled", fl_value_new_bool(value));
|
|
g_autoptr(GBytes) response = fl_message_codec_encode_message(
|
|
FL_MESSAGE_CODEC(codec), handledValue, nullptr);
|
|
send_response(
|
|
engine, message->channel, message->response_handle,
|
|
static_cast<const uint8_t*>(g_bytes_get_data(response, nullptr)),
|
|
g_bytes_get_size(response));
|
|
} else if (strcmp(message->channel, "test/key-event-delayed") == 0) {
|
|
static std::unique_ptr<const FlutterPlatformMessageResponseHandle>
|
|
delayed_response_handle = nullptr;
|
|
g_autoptr(FlJsonMessageCodec) codec = fl_json_message_codec_new();
|
|
g_autoptr(FlValue) handledValue = fl_value_new_map();
|
|
fl_value_set_string_take(handledValue, "handled", fl_value_new_bool(true));
|
|
g_autoptr(GBytes) response = fl_message_codec_encode_message(
|
|
FL_MESSAGE_CODEC(codec), handledValue, nullptr);
|
|
if (delayed_response_handle == nullptr) {
|
|
delayed_response_handle.reset(message->response_handle);
|
|
} else {
|
|
send_response(
|
|
engine, message->channel, message->response_handle,
|
|
static_cast<const uint8_t*>(g_bytes_get_data(response, nullptr)),
|
|
g_bytes_get_size(response));
|
|
send_response(
|
|
engine, message->channel, delayed_response_handle.release(),
|
|
static_cast<const uint8_t*>(g_bytes_get_data(response, nullptr)),
|
|
g_bytes_get_size(response));
|
|
}
|
|
}
|
|
|
|
return kSuccess;
|
|
}
|
|
|
|
FlutterEngineResult FlutterPlatformMessageCreateResponseHandle(
|
|
FLUTTER_API_SYMBOL(FlutterEngine) engine,
|
|
FlutterDataCallback data_callback,
|
|
void* user_data,
|
|
FlutterPlatformMessageResponseHandle** response_out) {
|
|
EXPECT_TRUE(engine->running);
|
|
EXPECT_NE(data_callback, nullptr);
|
|
EXPECT_NE(user_data, nullptr);
|
|
|
|
_FlutterPlatformMessageResponseHandle* handle =
|
|
new _FlutterPlatformMessageResponseHandle(data_callback, user_data);
|
|
|
|
*response_out = handle;
|
|
return kSuccess;
|
|
}
|
|
|
|
FlutterEngineResult FlutterPlatformMessageReleaseResponseHandle(
|
|
FLUTTER_API_SYMBOL(FlutterEngine) engine,
|
|
FlutterPlatformMessageResponseHandle* response) {
|
|
EXPECT_NE(engine, nullptr);
|
|
EXPECT_NE(response, nullptr);
|
|
|
|
EXPECT_TRUE(engine->running);
|
|
|
|
EXPECT_FALSE(response->released);
|
|
response->released = true;
|
|
|
|
return kSuccess;
|
|
}
|
|
|
|
FlutterEngineResult FlutterEngineSendPlatformMessageResponse(
|
|
FLUTTER_API_SYMBOL(FlutterEngine) engine,
|
|
const FlutterPlatformMessageResponseHandle* handle,
|
|
const uint8_t* data,
|
|
size_t data_length) {
|
|
EXPECT_NE(engine, nullptr);
|
|
EXPECT_NE(handle, nullptr);
|
|
|
|
EXPECT_TRUE(engine->running);
|
|
|
|
// Send a message so the shell can check the responses received.
|
|
if (handle->channel != "test/responses") {
|
|
send_message(engine, "test/responses", data, data_length);
|
|
}
|
|
|
|
EXPECT_FALSE(handle->released);
|
|
|
|
delete handle;
|
|
|
|
return kSuccess;
|
|
}
|
|
|
|
FlutterEngineResult FlutterEngineRunTask(FLUTTER_API_SYMBOL(FlutterEngine)
|
|
engine,
|
|
const FlutterTask* task) {
|
|
EXPECT_NE(engine, nullptr);
|
|
EXPECT_NE(task, nullptr);
|
|
EXPECT_NE(task->runner, nullptr);
|
|
|
|
FlutterTaskRunner runner = task->runner;
|
|
EXPECT_NE(runner, nullptr);
|
|
const FlutterPlatformMessageResponseHandle* response_handle =
|
|
runner->response_handle;
|
|
if (response_handle != nullptr) {
|
|
EXPECT_NE(response_handle->data_callback, nullptr);
|
|
response_handle->data_callback(runner->message, runner->message_size,
|
|
response_handle->user_data);
|
|
} else {
|
|
_FlutterPlatformMessageResponseHandle* handle =
|
|
new _FlutterPlatformMessageResponseHandle(runner->channel);
|
|
|
|
FlutterPlatformMessage message;
|
|
message.struct_size = sizeof(FlutterPlatformMessage);
|
|
message.channel = runner->channel.c_str();
|
|
message.message = runner->message;
|
|
message.message_size = runner->message_size;
|
|
message.response_handle = handle;
|
|
engine->platform_message_callback(&message, engine->user_data);
|
|
}
|
|
|
|
delete runner;
|
|
|
|
return kSuccess;
|
|
}
|
|
|
|
bool FlutterEngineRunsAOTCompiledDartCode() {
|
|
return false;
|
|
}
|
|
|
|
FlutterEngineResult FlutterEngineUpdateLocales(FLUTTER_API_SYMBOL(FlutterEngine)
|
|
engine,
|
|
const FlutterLocale** locales,
|
|
size_t locales_count) {
|
|
return kSuccess;
|
|
}
|
|
|
|
FlutterEngineResult FlutterEngineUpdateSemanticsEnabled(
|
|
FLUTTER_API_SYMBOL(FlutterEngine) engine,
|
|
bool enabled) {
|
|
return kSuccess;
|
|
}
|
|
|
|
FlutterEngineResult FlutterEngineDispatchSemanticsAction(
|
|
FLUTTER_API_SYMBOL(FlutterEngine) engine,
|
|
uint64_t id,
|
|
FlutterSemanticsAction action,
|
|
const uint8_t* data,
|
|
size_t data_length) {
|
|
return kSuccess;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
FlutterEngineResult FlutterEngineGetProcAddresses(
|
|
FlutterEngineProcTable* table) {
|
|
if (!table) {
|
|
return kInvalidArguments;
|
|
}
|
|
|
|
FlutterEngineProcTable empty_table = {};
|
|
*table = empty_table;
|
|
|
|
table->CreateAOTData = &FlutterEngineCreateAOTData;
|
|
table->CollectAOTData = &FlutterEngineCollectAOTData;
|
|
table->Run = &FlutterEngineRun;
|
|
table->Shutdown = &FlutterEngineShutdown;
|
|
table->Initialize = &FlutterEngineInitialize;
|
|
table->Deinitialize = &FlutterEngineDeinitialize;
|
|
table->RunInitialized = &FlutterEngineRunInitialized;
|
|
table->SendWindowMetricsEvent = &FlutterEngineSendWindowMetricsEvent;
|
|
table->SendPointerEvent = &FlutterEngineSendPointerEvent;
|
|
table->SendPlatformMessage = &FlutterEngineSendPlatformMessage;
|
|
table->PlatformMessageCreateResponseHandle =
|
|
&FlutterPlatformMessageCreateResponseHandle;
|
|
table->PlatformMessageReleaseResponseHandle =
|
|
&FlutterPlatformMessageReleaseResponseHandle;
|
|
table->SendPlatformMessageResponse =
|
|
&FlutterEngineSendPlatformMessageResponse;
|
|
table->RunTask = &FlutterEngineRunTask;
|
|
table->UpdateLocales = &FlutterEngineUpdateLocales;
|
|
table->UpdateSemanticsEnabled = &FlutterEngineUpdateSemanticsEnabled;
|
|
table->DispatchSemanticsAction = &FlutterEngineDispatchSemanticsAction;
|
|
table->RunsAOTCompiledDartCode = &FlutterEngineRunsAOTCompiledDartCode;
|
|
|
|
return kSuccess;
|
|
}
|