// 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 #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( g_object_new(fl_json_method_codec_get_type(), nullptr)); }