diff --git a/shell/platform/linux/fl_standard_method_codec.cc b/shell/platform/linux/fl_standard_method_codec.cc index 5249ee8416187..46a803a041aaa 100644 --- a/shell/platform/linux/fl_standard_method_codec.cc +++ b/shell/platform/linux/fl_standard_method_codec.cc @@ -18,17 +18,52 @@ static constexpr guint8 kEnvelopeTypeError = 1; struct _FlStandardMethodCodec { FlMethodCodec parent_instance; - FlStandardMessageCodec* codec; + FlStandardMessageCodec* message_codec; }; +enum { kPropMessageCodec = 1, kPropLast }; + G_DEFINE_TYPE(FlStandardMethodCodec, fl_standard_method_codec, fl_method_codec_get_type()) +static void fl_standard_method_codec_set_property(GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec) { + FlStandardMethodCodec* self = FL_STANDARD_METHOD_CODEC(object); + + switch (prop_id) { + case kPropMessageCodec: + g_set_object(&self->message_codec, + FL_STANDARD_MESSAGE_CODEC(g_value_get_object(value))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void fl_standard_method_codec_get_property(GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec) { + FlStandardMethodCodec* self = FL_STANDARD_METHOD_CODEC(object); + + switch (prop_id) { + case kPropMessageCodec: + g_value_set_object(value, self->message_codec); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + static void fl_standard_method_codec_dispose(GObject* object) { FlStandardMethodCodec* self = FL_STANDARD_METHOD_CODEC(object); - g_clear_object(&self->codec); + g_clear_object(&self->message_codec); G_OBJECT_CLASS(fl_standard_method_codec_parent_class)->dispose(object); } @@ -42,11 +77,11 @@ static GBytes* fl_standard_method_codec_encode_method_call(FlMethodCodec* codec, g_autoptr(GByteArray) buffer = g_byte_array_new(); g_autoptr(FlValue) name_value = fl_value_new_string(name); - if (!fl_standard_message_codec_write_value(self->codec, buffer, name_value, - error)) { + if (!fl_standard_message_codec_write_value(self->message_codec, buffer, + name_value, error)) { return nullptr; } - if (!fl_standard_message_codec_write_value(self->codec, buffer, args, + if (!fl_standard_message_codec_write_value(self->message_codec, buffer, args, error)) { return nullptr; } @@ -66,7 +101,7 @@ static gboolean fl_standard_method_codec_decode_method_call( size_t offset = 0; g_autoptr(FlValue) name_value = fl_standard_message_codec_read_value( - self->codec, message, &offset, error); + self->message_codec, message, &offset, error); if (name_value == nullptr) { return FALSE; } @@ -77,7 +112,7 @@ static gboolean fl_standard_method_codec_decode_method_call( } g_autoptr(FlValue) args_value = fl_standard_message_codec_read_value( - self->codec, message, &offset, error); + self->message_codec, message, &offset, error); if (args_value == nullptr) { return FALSE; } @@ -104,8 +139,8 @@ static GBytes* fl_standard_method_codec_encode_success_envelope( g_autoptr(GByteArray) buffer = g_byte_array_new(); guint8 type = kEnvelopeTypeSuccess; g_byte_array_append(buffer, &type, 1); - if (!fl_standard_message_codec_write_value(self->codec, buffer, result, - error)) { + if (!fl_standard_message_codec_write_value(self->message_codec, buffer, + result, error)) { return nullptr; } @@ -126,18 +161,18 @@ static GBytes* fl_standard_method_codec_encode_error_envelope( guint8 type = kEnvelopeTypeError; g_byte_array_append(buffer, &type, 1); g_autoptr(FlValue) code_value = fl_value_new_string(code); - if (!fl_standard_message_codec_write_value(self->codec, buffer, code_value, - error)) { + if (!fl_standard_message_codec_write_value(self->message_codec, buffer, + code_value, error)) { return nullptr; } g_autoptr(FlValue) message_value = message != nullptr ? fl_value_new_string(message) : nullptr; - if (!fl_standard_message_codec_write_value(self->codec, buffer, message_value, - error)) { + if (!fl_standard_message_codec_write_value(self->message_codec, buffer, + message_value, error)) { return nullptr; } - if (!fl_standard_message_codec_write_value(self->codec, buffer, details, - error)) { + if (!fl_standard_message_codec_write_value(self->message_codec, buffer, + details, error)) { return nullptr; } @@ -167,7 +202,7 @@ static FlMethodResponse* fl_standard_method_codec_decode_response( g_autoptr(FlMethodResponse) response = nullptr; if (type == kEnvelopeTypeError) { g_autoptr(FlValue) code = fl_standard_message_codec_read_value( - self->codec, message, &offset, error); + self->message_codec, message, &offset, error); if (code == nullptr) { return nullptr; } @@ -178,7 +213,7 @@ static FlMethodResponse* fl_standard_method_codec_decode_response( } g_autoptr(FlValue) error_message = fl_standard_message_codec_read_value( - self->codec, message, &offset, error); + self->message_codec, message, &offset, error); if (error_message == nullptr) { return nullptr; } @@ -190,7 +225,7 @@ static FlMethodResponse* fl_standard_method_codec_decode_response( } g_autoptr(FlValue) details = fl_standard_message_codec_read_value( - self->codec, message, &offset, error); + self->message_codec, message, &offset, error); if (details == nullptr) { return nullptr; } @@ -203,7 +238,7 @@ static FlMethodResponse* fl_standard_method_codec_decode_response( fl_value_get_type(details) != FL_VALUE_TYPE_NULL ? details : nullptr)); } else if (type == kEnvelopeTypeSuccess) { g_autoptr(FlValue) result = fl_standard_message_codec_read_value( - self->codec, message, &offset, error); + self->message_codec, message, &offset, error); if (result == nullptr) { return nullptr; @@ -227,7 +262,10 @@ static FlMethodResponse* fl_standard_method_codec_decode_response( static void fl_standard_method_codec_class_init( FlStandardMethodCodecClass* klass) { + G_OBJECT_CLASS(klass)->set_property = fl_standard_method_codec_set_property; + G_OBJECT_CLASS(klass)->get_property = fl_standard_method_codec_get_property; G_OBJECT_CLASS(klass)->dispose = fl_standard_method_codec_dispose; + FL_METHOD_CODEC_CLASS(klass)->encode_method_call = fl_standard_method_codec_encode_method_call; FL_METHOD_CODEC_CLASS(klass)->decode_method_call = @@ -238,13 +276,28 @@ static void fl_standard_method_codec_class_init( fl_standard_method_codec_encode_error_envelope; FL_METHOD_CODEC_CLASS(klass)->decode_response = fl_standard_method_codec_decode_response; -} -static void fl_standard_method_codec_init(FlStandardMethodCodec* self) { - self->codec = fl_standard_message_codec_new(); + g_object_class_install_property( + G_OBJECT_CLASS(klass), kPropMessageCodec, + g_param_spec_object( + "message-codec", "message-codec", "Message codec to use", + fl_message_codec_get_type(), + static_cast(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS))); } +static void fl_standard_method_codec_init(FlStandardMethodCodec* self) {} + G_MODULE_EXPORT FlStandardMethodCodec* fl_standard_method_codec_new() { + g_autoptr(FlStandardMessageCodec) message_codec = + fl_standard_message_codec_new(); + return fl_standard_method_codec_new_with_message_codec(message_codec); +} + +G_MODULE_EXPORT FlStandardMethodCodec* +fl_standard_method_codec_new_with_message_codec( + FlStandardMessageCodec* message_codec) { return FL_STANDARD_METHOD_CODEC( - g_object_new(fl_standard_method_codec_get_type(), nullptr)); + g_object_new(fl_standard_method_codec_get_type(), "message-codec", + message_codec, nullptr)); } diff --git a/shell/platform/linux/fl_standard_method_codec_test.cc b/shell/platform/linux/fl_standard_method_codec_test.cc index 8309b7bccc2db..1685a7bf7de4d 100644 --- a/shell/platform/linux/fl_standard_method_codec_test.cc +++ b/shell/platform/linux/fl_standard_method_codec_test.cc @@ -5,9 +5,112 @@ #include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h" #include "flutter/shell/platform/linux/fl_method_codec_private.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_message_codec.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_message_codec.h" #include "flutter/shell/platform/linux/testing/fl_test.h" #include "gtest/gtest.h" +G_DECLARE_FINAL_TYPE(FlTestMethodMessageCodec, + fl_test_method_message_codec, + FL, + TEST_METHOD_MESSAGE_CODEC, + FlStandardMessageCodec) + +struct _FlTestMethodMessageCodec { + FlStandardMessageCodec parent_instance; +}; + +G_DEFINE_TYPE(FlTestMethodMessageCodec, + fl_test_method_message_codec, + fl_standard_message_codec_get_type()) + +static gboolean write_custom_value(FlStandardMessageCodec* codec, + GByteArray* buffer, + FlValue* value, + GError** error) { + const gchar* text = + static_cast(fl_value_get_custom_value(value)); + size_t length = strlen(text); + + uint8_t type = 128; + g_byte_array_append(buffer, &type, sizeof(uint8_t)); + fl_standard_message_codec_write_size(codec, buffer, length); + g_byte_array_append(buffer, reinterpret_cast(text), length); + return TRUE; +} + +static gboolean fl_test_method_message_codec_write_value( + FlStandardMessageCodec* codec, + GByteArray* buffer, + FlValue* value, + GError** error) { + if (fl_value_get_type(value) == FL_VALUE_TYPE_CUSTOM && + fl_value_get_custom_type(value) == 128) { + return write_custom_value(codec, buffer, value, error); + } else { + return FL_STANDARD_MESSAGE_CODEC_CLASS( + fl_test_method_message_codec_parent_class) + ->write_value(codec, buffer, value, error); + } +} + +static FlValue* read_custom_value(FlStandardMessageCodec* codec, + GBytes* buffer, + size_t* offset, + GError** error) { + uint32_t length; + if (!fl_standard_message_codec_read_size(codec, buffer, offset, &length, + error)) { + return nullptr; + } + if (*offset + length > g_bytes_get_size(buffer)) { + g_set_error(error, FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA, "Unexpected end of data"); + return nullptr; + } + FlValue* value = fl_value_new_custom( + 128, + g_strndup(static_cast(g_bytes_get_data(buffer, nullptr)) + + *offset, + length), + g_free); + *offset += length; + + return value; +} + +static FlValue* fl_test_method_message_codec_read_value_of_type( + FlStandardMessageCodec* codec, + GBytes* buffer, + size_t* offset, + int type, + GError** error) { + if (type == 128) { + return read_custom_value(codec, buffer, offset, error); + } else { + return FL_STANDARD_MESSAGE_CODEC_CLASS( + fl_test_method_message_codec_parent_class) + ->read_value_of_type(codec, buffer, offset, type, error); + } +} + +static void fl_test_method_message_codec_class_init( + FlTestMethodMessageCodecClass* klass) { + FL_STANDARD_MESSAGE_CODEC_CLASS(klass)->write_value = + fl_test_method_message_codec_write_value; + FL_STANDARD_MESSAGE_CODEC_CLASS(klass)->read_value_of_type = + fl_test_method_message_codec_read_value_of_type; +} + +static void fl_test_method_message_codec_init(FlTestMethodMessageCodec* self) { + // The following line suppresses a warning for unused function + FL_IS_TEST_METHOD_MESSAGE_CODEC(self); +} + +static FlTestMethodMessageCodec* fl_test_method_message_codec_new() { + return FL_TEST_METHOD_MESSAGE_CODEC( + g_object_new(fl_test_method_message_codec_get_type(), nullptr)); +} + // NOTE(robert-ancell) These test cases assumes a little-endian architecture. // These tests will need to be updated if tested on a big endian architecture. @@ -375,3 +478,32 @@ TEST(FlStandardMethodCodecTest, DecodeResponseUnknownEnvelope) { decode_error_response("02", FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED); } + +TEST(FlStandardMethodCodecTest, CustomMessageCodec) { + g_autoptr(FlTestMethodMessageCodec) message_codec = + fl_test_method_message_codec_new(); + g_autoptr(FlStandardMethodCodec) codec = + fl_standard_method_codec_new_with_message_codec( + FL_STANDARD_MESSAGE_CODEC(message_codec)); + + g_autoptr(GError) error = nullptr; + g_autoptr(FlValue) value = fl_value_new_custom(128, "hello", nullptr); + g_autoptr(GBytes) message = fl_method_codec_encode_success_envelope( + FL_METHOD_CODEC(codec), value, &error); + EXPECT_NE(message, nullptr); + EXPECT_EQ(error, nullptr); + g_autofree gchar* hex_string = bytes_to_hex_string(message); + EXPECT_STREQ(hex_string, "00800568656c6c6f"); + + g_autoptr(FlMethodResponse) response = + fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error); + EXPECT_NE(response, nullptr); + EXPECT_EQ(error, nullptr); + EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response)); + FlValue* result = fl_method_success_response_get_result( + FL_METHOD_SUCCESS_RESPONSE(response)); + ASSERT_EQ(fl_value_get_type(result), FL_VALUE_TYPE_CUSTOM); + ASSERT_EQ(fl_value_get_custom_type(result), 128); + EXPECT_STREQ(static_cast(fl_value_get_custom_value(result)), + "hello"); +} diff --git a/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h b/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h index a19886cdbde9f..ba3447bfe2ab5 100644 --- a/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h +++ b/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h @@ -12,6 +12,7 @@ #include #include "fl_method_codec.h" +#include "fl_standard_message_codec.h" G_BEGIN_DECLS @@ -42,6 +43,17 @@ G_DECLARE_FINAL_TYPE(FlStandardMethodCodec, */ FlStandardMethodCodec* fl_standard_method_codec_new(); +/** + * fl_standard_method_codec_new: + * @message_codec: A #FlMessageCodec. + * + * Creates an #FlStandardMethodCodec with a custom message codec. + * + * Returns: a new #FlStandardMethodCodec. + */ +FlStandardMethodCodec* fl_standard_method_codec_new_with_message_codec( + FlStandardMessageCodec* message_codec); + G_END_DECLS #endif // FLUTTER_SHELL_PLATFORM_LINUX_PUBLIC_FLUTTER_LINUX_FL_STANDARD_METHOD_CODEC_H_