| // 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 "flutter/shell/platform/linux/public/flutter_linux/fl_json_method_codec.h" |
| |
| #include <gmodule.h> |
| |
| #include "flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h" |
| |
| static constexpr char kMethodKey[] = "method"; |
| static constexpr char kArgsKey[] = "args"; |
| |
| struct _FlJsonMethodCodec { |
| FlMethodCodec parent_instance; |
| |
| FlJsonMessageCodec* codec; |
| }; |
| |
| G_DEFINE_TYPE(FlJsonMethodCodec, |
| fl_json_method_codec, |
| fl_method_codec_get_type()) |
| |
| static void fl_json_method_codec_dispose(GObject* object) { |
| FlJsonMethodCodec* self = FL_JSON_METHOD_CODEC(object); |
| |
| g_clear_object(&self->codec); |
| |
| G_OBJECT_CLASS(fl_json_method_codec_parent_class)->dispose(object); |
| } |
| |
| // Implements FlMethodCodec::encode_method_call. |
| static GBytes* fl_json_method_codec_encode_method_call(FlMethodCodec* codec, |
| const gchar* name, |
| FlValue* args, |
| GError** error) { |
| FlJsonMethodCodec* self = FL_JSON_METHOD_CODEC(codec); |
| |
| g_autoptr(FlValue) message = fl_value_new_map(); |
| fl_value_set_take(message, fl_value_new_string(kMethodKey), |
| fl_value_new_string(name)); |
| fl_value_set_take(message, fl_value_new_string(kArgsKey), |
| args != nullptr ? fl_value_ref(args) : fl_value_new_null()); |
| |
| return fl_message_codec_encode_message(FL_MESSAGE_CODEC(self->codec), message, |
| error); |
| } |
| |
| // Implements FlMethodCodec::decode_method_call. |
| static gboolean fl_json_method_codec_decode_method_call(FlMethodCodec* codec, |
| GBytes* message, |
| gchar** name, |
| FlValue** args, |
| GError** error) { |
| FlJsonMethodCodec* self = FL_JSON_METHOD_CODEC(codec); |
| |
| g_autoptr(FlValue) value = fl_message_codec_decode_message( |
| FL_MESSAGE_CODEC(self->codec), message, error); |
| if (value == nullptr) { |
| return FALSE; |
| } |
| |
| if (fl_value_get_type(value) != FL_VALUE_TYPE_MAP) { |
| g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED, |
| "Expected JSON map in method response, got %d instead", |
| fl_value_get_type(value)); |
| return FALSE; |
| } |
| |
| FlValue* method_value = fl_value_lookup_string(value, kMethodKey); |
| if (method_value == nullptr) { |
| g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED, |
| "Missing JSON method field in method response"); |
| return FALSE; |
| } |
| if (fl_value_get_type(method_value) != FL_VALUE_TYPE_STRING) { |
| g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED, |
| "Expected JSON string for method name, got %d instead", |
| fl_value_get_type(method_value)); |
| return FALSE; |
| } |
| FlValue* args_value = fl_value_lookup_string(value, kArgsKey); |
| |
| *name = g_strdup(fl_value_get_string(method_value)); |
| *args = args_value != nullptr ? fl_value_ref(args_value) : nullptr; |
| |
| return TRUE; |
| } |
| |
| // Implements FlMethodCodec::encode_success_envelope. |
| static GBytes* fl_json_method_codec_encode_success_envelope( |
| FlMethodCodec* codec, |
| FlValue* result, |
| GError** error) { |
| FlJsonMethodCodec* self = FL_JSON_METHOD_CODEC(codec); |
| |
| g_autoptr(FlValue) message = fl_value_new_list(); |
| fl_value_append_take( |
| message, result != nullptr ? fl_value_ref(result) : fl_value_new_null()); |
| |
| return fl_message_codec_encode_message(FL_MESSAGE_CODEC(self->codec), message, |
| error); |
| } |
| |
| // Implements FlMethodCodec::encode_error_envelope. |
| static GBytes* fl_json_method_codec_encode_error_envelope( |
| FlMethodCodec* codec, |
| const gchar* code, |
| const gchar* error_message, |
| FlValue* details, |
| GError** error) { |
| FlJsonMethodCodec* self = FL_JSON_METHOD_CODEC(codec); |
| |
| g_autoptr(FlValue) message = fl_value_new_list(); |
| fl_value_append_take(message, fl_value_new_string(code)); |
| fl_value_append_take(message, error_message != nullptr |
| ? fl_value_new_string(error_message) |
| : fl_value_new_null()); |
| fl_value_append_take(message, details != nullptr ? fl_value_ref(details) |
| : fl_value_new_null()); |
| |
| return fl_message_codec_encode_message(FL_MESSAGE_CODEC(self->codec), message, |
| error); |
| } |
| |
| // Implements FlMethodCodec::decode_response. |
| static FlMethodResponse* fl_json_method_codec_decode_response( |
| FlMethodCodec* codec, |
| GBytes* message, |
| GError** error) { |
| FlJsonMethodCodec* self = FL_JSON_METHOD_CODEC(codec); |
| |
| g_autoptr(FlValue) value = fl_message_codec_decode_message( |
| FL_MESSAGE_CODEC(self->codec), message, error); |
| if (value == nullptr) { |
| return nullptr; |
| } |
| |
| if (fl_value_get_type(value) != FL_VALUE_TYPE_LIST) { |
| g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED, |
| "Expected JSON list in method response, got %d instead", |
| fl_value_get_type(value)); |
| return nullptr; |
| } |
| |
| size_t length = fl_value_get_length(value); |
| if (length == 1) { |
| return FL_METHOD_RESPONSE( |
| fl_method_success_response_new(fl_value_get_list_value(value, 0))); |
| } else if (length == 3) { |
| FlValue* code_value = fl_value_get_list_value(value, 0); |
| if (fl_value_get_type(code_value) != FL_VALUE_TYPE_STRING) { |
| g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED, |
| "Error code wrong type"); |
| return nullptr; |
| } |
| const gchar* code = fl_value_get_string(code_value); |
| |
| FlValue* message_value = fl_value_get_list_value(value, 1); |
| if (fl_value_get_type(message_value) != FL_VALUE_TYPE_STRING && |
| fl_value_get_type(message_value) != FL_VALUE_TYPE_NULL) { |
| g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED, |
| "Error message wrong type"); |
| return nullptr; |
| } |
| const gchar* message = |
| fl_value_get_type(message_value) == FL_VALUE_TYPE_STRING |
| ? fl_value_get_string(message_value) |
| : nullptr; |
| |
| FlValue* args = fl_value_get_list_value(value, 2); |
| if (fl_value_get_type(args) == FL_VALUE_TYPE_NULL) { |
| args = nullptr; |
| } |
| |
| return FL_METHOD_RESPONSE( |
| fl_method_error_response_new(code, message, args)); |
| } else { |
| g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED, |
| "Got response envelope of length %zi, expected 1 (success) or " |
| "3 (error)", |
| length); |
| return nullptr; |
| } |
| } |
| |
| static void fl_json_method_codec_class_init(FlJsonMethodCodecClass* klass) { |
| G_OBJECT_CLASS(klass)->dispose = fl_json_method_codec_dispose; |
| FL_METHOD_CODEC_CLASS(klass)->encode_method_call = |
| fl_json_method_codec_encode_method_call; |
| FL_METHOD_CODEC_CLASS(klass)->decode_method_call = |
| fl_json_method_codec_decode_method_call; |
| FL_METHOD_CODEC_CLASS(klass)->encode_success_envelope = |
| fl_json_method_codec_encode_success_envelope; |
| FL_METHOD_CODEC_CLASS(klass)->encode_error_envelope = |
| fl_json_method_codec_encode_error_envelope; |
| FL_METHOD_CODEC_CLASS(klass)->decode_response = |
| fl_json_method_codec_decode_response; |
| } |
| |
| static void fl_json_method_codec_init(FlJsonMethodCodec* self) { |
| self->codec = fl_json_message_codec_new(); |
| } |
| |
| G_MODULE_EXPORT FlJsonMethodCodec* fl_json_method_codec_new() { |
| return static_cast<FlJsonMethodCodec*>( |
| g_object_new(fl_json_method_codec_get_type(), nullptr)); |
| } |