Skip to content

Commit bf25922

Browse files
Switch Linux embedding to proc table embedder API (flutter#22280)
Switches the Linux embedding from the standard C API to the new proctable version, to allow for unit testing of the embedding layer separately from the embedder APIs implementation.
1 parent d6ac456 commit bf25922

File tree

5 files changed

+168
-52
lines changed

5 files changed

+168
-52
lines changed

shell/platform/linux/BUILD.gn

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,10 @@ source_set("flutter_linux_sources") {
119119
]
120120

121121
# Set flag to stop headers being directly included (library users should not do this)
122-
defines = [ "FLUTTER_LINUX_COMPILATION" ]
122+
defines = [
123+
"FLUTTER_LINUX_COMPILATION",
124+
"FLUTTER_ENGINE_NO_PROTOTYPES",
125+
]
123126

124127
deps = [
125128
"//flutter/shell/platform/common/cpp:common_cpp_input",
@@ -138,6 +141,8 @@ source_set("flutter_linux") {
138141
"//third_party/khronos:khronos_headers",
139142
]
140143

144+
defines = [ "FLUTTER_ENGINE_NO_PROTOTYPES" ]
145+
141146
public_deps = [ ":flutter_linux_sources" ]
142147

143148
deps = [ "//flutter/shell/platform/embedder:embedder_as_internal_library" ]
@@ -182,14 +187,20 @@ executable("flutter_linux_unittests") {
182187
"//flutter/shell/platform/linux/config:x11",
183188
]
184189

185-
# Set flag to allow public headers to be directly included (library users should not do this)
186-
defines = [ "FLUTTER_LINUX_COMPILATION" ]
190+
defines = [
191+
"FLUTTER_ENGINE_NO_PROTOTYPES",
192+
193+
# Set flag to allow public headers to be directly included
194+
# (library users should not do this)
195+
"FLUTTER_LINUX_COMPILATION",
196+
]
187197

188198
deps = [
189199
":flutter_linux_fixtures",
190200
":flutter_linux_sources",
191201
"//flutter/runtime:libdart",
192202
"//flutter/shell/platform/embedder:embedder_headers",
203+
"//flutter/shell/platform/embedder:embedder_test_utils",
193204
"//flutter/testing",
194205
]
195206
}

shell/platform/linux/fl_basic_message_channel_test.cc

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,54 @@
55
// Included first as it collides with the X11 headers.
66
#include "gtest/gtest.h"
77

8+
#include "flutter/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h"
9+
10+
#include "flutter/shell/platform/embedder/embedder.h"
11+
#include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
812
#include "flutter/shell/platform/linux/fl_binary_messenger_private.h"
913
#include "flutter/shell/platform/linux/fl_engine_private.h"
10-
#include "flutter/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h"
14+
#include "flutter/shell/platform/linux/public/flutter_linux/fl_message_codec.h"
1115
#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_message_codec.h"
1216
#include "flutter/shell/platform/linux/testing/fl_test.h"
1317
#include "flutter/shell/platform/linux/testing/mock_renderer.h"
1418

19+
// Checks sending a message without a reponse works.
20+
TEST(FlBasicMessageChannelTest, SendMessageWithoutResponse) {
21+
g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
22+
23+
g_autoptr(FlEngine) engine = make_mock_engine();
24+
FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
25+
26+
bool called = false;
27+
embedder_api->SendPlatformMessage = MOCK_ENGINE_PROC(
28+
SendPlatformMessage,
29+
([&called](auto engine, const FlutterPlatformMessage* message) {
30+
called = true;
31+
EXPECT_STREQ(message->channel, "test");
32+
EXPECT_EQ(message->response_handle, nullptr);
33+
34+
g_autoptr(GBytes) message_bytes =
35+
g_bytes_new(message->message, message->message_size);
36+
g_autoptr(FlStandardMessageCodec) codec =
37+
fl_standard_message_codec_new();
38+
FlValue* message_value = fl_message_codec_decode_message(
39+
FL_MESSAGE_CODEC(codec), message_bytes, nullptr);
40+
EXPECT_EQ(fl_value_get_type(message_value), FL_VALUE_TYPE_STRING);
41+
EXPECT_STREQ(fl_value_get_string(message_value), "Hello World!");
42+
43+
return kSuccess;
44+
}));
45+
46+
FlBinaryMessenger* messenger = fl_binary_messenger_new(engine);
47+
g_autoptr(FlStandardMessageCodec) codec = fl_standard_message_codec_new();
48+
g_autoptr(FlBasicMessageChannel) channel =
49+
fl_basic_message_channel_new(messenger, "test", FL_MESSAGE_CODEC(codec));
50+
g_autoptr(FlValue) message = fl_value_new_string("Hello World!");
51+
fl_basic_message_channel_send(channel, message, nullptr, nullptr, loop);
52+
53+
EXPECT_TRUE(called);
54+
}
55+
1556
// Called when the message response is received in the SendMessage test.
1657
static void echo_response_cb(GObject* object,
1758
GAsyncResult* result,
@@ -28,8 +69,8 @@ static void echo_response_cb(GObject* object,
2869
g_main_loop_quit(static_cast<GMainLoop*>(user_data));
2970
}
3071

31-
// Checks sending a message works.
32-
TEST(FlBasicMessageChannelTest, SendMessage) {
72+
// Checks sending a message with a response works.
73+
TEST(FlBasicMessageChannelTest, SendMessageWithResponse) {
3374
g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
3475

3576
g_autoptr(FlEngine) engine = make_mock_engine();

shell/platform/linux/fl_engine.cc

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ struct _FlEngine {
3232
FlBinaryMessenger* binary_messenger;
3333
FlutterEngineAOTData aot_data;
3434
FLUTTER_API_SYMBOL(FlutterEngine) engine;
35+
FlutterEngineProcTable embedder_api;
3536

3637
// Function to call when a platform message is received.
3738
FlEnginePlatformMessageHandler platform_message_handler;
@@ -127,7 +128,7 @@ static void setup_locales(FlEngine* self) {
127128
}
128129
FlutterLocale** locales =
129130
reinterpret_cast<FlutterLocale**>(locales_array->pdata);
130-
FlutterEngineResult result = FlutterEngineUpdateLocales(
131+
FlutterEngineResult result = self->embedder_api.UpdateLocales(
131132
self->engine, const_cast<const FlutterLocale**>(locales),
132133
locales_array->len);
133134
if (result != kSuccess) {
@@ -143,7 +144,7 @@ static gboolean flutter_source_dispatch(GSource* source,
143144
FlEngine* self = fl_source->engine;
144145

145146
FlutterEngineResult result =
146-
FlutterEngineRunTask(self->engine, &fl_source->task);
147+
self->embedder_api.RunTask(self->engine, &fl_source->task);
147148
if (result != kSuccess) {
148149
g_warning("Failed to run Flutter task\n");
149150
}
@@ -302,12 +303,12 @@ static void fl_engine_dispose(GObject* object) {
302303
FlEngine* self = FL_ENGINE(object);
303304

304305
if (self->engine != nullptr) {
305-
FlutterEngineShutdown(self->engine);
306+
self->embedder_api.Shutdown(self->engine);
306307
self->engine = nullptr;
307308
}
308309

309310
if (self->aot_data != nullptr) {
310-
FlutterEngineCollectAOTData(self->aot_data);
311+
self->embedder_api.CollectAOTData(self->aot_data);
311312
self->aot_data = nullptr;
312313
}
313314

@@ -332,6 +333,9 @@ static void fl_engine_class_init(FlEngineClass* klass) {
332333
static void fl_engine_init(FlEngine* self) {
333334
self->thread = g_thread_self();
334335

336+
self->embedder_api.struct_size = sizeof(FlutterEngineProcTable);
337+
FlutterEngineGetProcAddresses(&self->embedder_api);
338+
335339
self->binary_messenger = fl_binary_messenger_new(self);
336340
}
337341

@@ -400,27 +404,28 @@ gboolean fl_engine_start(FlEngine* self, GError** error) {
400404
args.dart_entrypoint_argv =
401405
reinterpret_cast<const char* const*>(dart_entrypoint_args);
402406

403-
if (FlutterEngineRunsAOTCompiledDartCode()) {
407+
if (self->embedder_api.RunsAOTCompiledDartCode()) {
404408
FlutterEngineAOTDataSource source = {};
405409
source.type = kFlutterEngineAOTDataSourceTypeElfPath;
406410
source.elf_path = fl_dart_project_get_aot_library_path(self->project);
407-
if (FlutterEngineCreateAOTData(&source, &self->aot_data) != kSuccess) {
411+
if (self->embedder_api.CreateAOTData(&source, &self->aot_data) !=
412+
kSuccess) {
408413
g_set_error(error, fl_engine_error_quark(), FL_ENGINE_ERROR_FAILED,
409414
"Failed to create AOT data");
410415
return FALSE;
411416
}
412417
args.aot_data = self->aot_data;
413418
}
414419

415-
FlutterEngineResult result = FlutterEngineInitialize(
420+
FlutterEngineResult result = self->embedder_api.Initialize(
416421
FLUTTER_ENGINE_VERSION, &config, &args, self, &self->engine);
417422
if (result != kSuccess) {
418423
g_set_error(error, fl_engine_error_quark(), FL_ENGINE_ERROR_FAILED,
419424
"Failed to initialize Flutter engine");
420425
return FALSE;
421426
}
422427

423-
result = FlutterEngineRunInitialized(self->engine);
428+
result = self->embedder_api.RunInitialized(self->engine);
424429
if (result != kSuccess) {
425430
g_set_error(error, fl_engine_error_quark(), FL_ENGINE_ERROR_FAILED,
426431
"Failed to run Flutter engine");
@@ -432,6 +437,10 @@ gboolean fl_engine_start(FlEngine* self, GError** error) {
432437
return TRUE;
433438
}
434439

440+
FlutterEngineProcTable* fl_engine_get_embedder_api(FlEngine* self) {
441+
return &(self->embedder_api);
442+
}
443+
435444
void fl_engine_set_platform_message_handler(
436445
FlEngine* self,
437446
FlEnginePlatformMessageHandler handler,
@@ -470,7 +479,7 @@ gboolean fl_engine_send_platform_message_response(
470479
data =
471480
static_cast<const uint8_t*>(g_bytes_get_data(response, &data_length));
472481
}
473-
FlutterEngineResult result = FlutterEngineSendPlatformMessageResponse(
482+
FlutterEngineResult result = self->embedder_api.SendPlatformMessageResponse(
474483
self->engine, handle, data, data_length);
475484

476485
if (result != kSuccess) {
@@ -501,9 +510,10 @@ void fl_engine_send_platform_message(FlEngine* self,
501510
return;
502511
}
503512

504-
FlutterEngineResult result = FlutterPlatformMessageCreateResponseHandle(
505-
self->engine, fl_engine_platform_message_response_cb, task,
506-
&response_handle);
513+
FlutterEngineResult result =
514+
self->embedder_api.PlatformMessageCreateResponseHandle(
515+
self->engine, fl_engine_platform_message_response_cb, task,
516+
&response_handle);
507517
if (result != kSuccess) {
508518
g_task_return_new_error(task, fl_engine_error_quark(),
509519
FL_ENGINE_ERROR_FAILED,
@@ -525,7 +535,7 @@ void fl_engine_send_platform_message(FlEngine* self,
525535
fl_message.message_size = message != nullptr ? g_bytes_get_size(message) : 0;
526536
fl_message.response_handle = response_handle;
527537
FlutterEngineResult result =
528-
FlutterEngineSendPlatformMessage(self->engine, &fl_message);
538+
self->embedder_api.SendPlatformMessage(self->engine, &fl_message);
529539

530540
if (result != kSuccess && task != nullptr) {
531541
g_task_return_new_error(task, fl_engine_error_quark(),
@@ -535,7 +545,8 @@ void fl_engine_send_platform_message(FlEngine* self,
535545
}
536546

537547
if (response_handle != nullptr) {
538-
FlutterPlatformMessageReleaseResponseHandle(self->engine, response_handle);
548+
self->embedder_api.PlatformMessageReleaseResponseHandle(self->engine,
549+
response_handle);
539550
}
540551
}
541552

@@ -563,7 +574,7 @@ void fl_engine_send_window_metrics_event(FlEngine* self,
563574
event.width = width;
564575
event.height = height;
565576
event.pixel_ratio = pixel_ratio;
566-
FlutterEngineSendWindowMetricsEvent(self->engine, &event);
577+
self->embedder_api.SendWindowMetricsEvent(self->engine, &event);
567578
}
568579

569580
void fl_engine_send_mouse_pointer_event(FlEngine* self,
@@ -593,7 +604,7 @@ void fl_engine_send_mouse_pointer_event(FlEngine* self,
593604
fl_event.scroll_delta_y = scroll_delta_y;
594605
fl_event.device_kind = kFlutterPointerDeviceKindMouse;
595606
fl_event.buttons = buttons;
596-
FlutterEngineSendPointerEvent(self->engine, &fl_event, 1);
607+
self->embedder_api.SendPointerEvent(self->engine, &fl_event, 1);
597608
}
598609

599610
G_MODULE_EXPORT FlBinaryMessenger* fl_engine_get_binary_messenger(

shell/platform/linux/fl_engine_private.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ typedef gboolean (*FlEnginePlatformMessageHandler)(
5555
*/
5656
FlEngine* fl_engine_new(FlDartProject* project, FlRenderer* renderer);
5757

58+
/**
59+
* fl_engine_get_embedder_api:
60+
* @engine: an #FlEngine.
61+
*
62+
* Gets the embedder API proc table, allowing modificiations for unit testing.
63+
*
64+
* Returns: a mutable pointer to the embedder API proc table.
65+
*/
66+
FlutterEngineProcTable* fl_engine_get_embedder_api(FlEngine* engine);
67+
5868
/**
5969
* fl_engine_set_platform_message_handler:
6070
* @engine: an #FlEngine.

0 commit comments

Comments
 (0)