| // 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. | |
| #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_method_response.h" | |
| #include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h" | |
| #include "gtest/gtest.h" | |
| 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); | |
| } | |
| }; | |
| // 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 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 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 FlutterEngineDeinitialize(FLUTTER_API_SYMBOL(FlutterEngine) | |
| engine) { | |
| return kSuccess; | |
| } | |
| FlutterEngineResult FlutterEngineRunInitialized( | |
| FLUTTER_API_SYMBOL(FlutterEngine) engine) { | |
| engine->running = true; | |
| 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/failure") == 0) { | |
| // Generates an internal error. | |
| return kInternalInconsistency; | |
| } | |
| 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; | |
| } |