// 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 #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(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(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(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(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 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(g_bytes_get_data(response, nullptr)), g_bytes_get_size(response)); send_response( engine, message->channel, delayed_response_handle.release(), static_cast(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; }