Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Replaces the synchronous plugin response using raw data with an asynchronous response using a much higher-level abstraction that makes sending non-empty responses much easier. This makes the Linux plugin API consistent with the current state of the macOS plugin API. It follows Flutter's Java plugin API response object pattern, since without RTTI the ObjC approach of determining response type in a single callback isn't viable. As with the macOS rework, this is an incremental change. A future change will further align the macOS API with mobile, but this portion makes most of the changes that would significantly change the structure of a plugin (allowing for rich, async responses). This addresses issue #45 and issue #13
- Loading branch information
1 parent
52adb83
commit 4c3db78
Showing
18 changed files
with
424 additions
and
227 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
BasedOnStyle: Google | ||
--- | ||
Language: Cpp | ||
DerivePointerAlignment: false | ||
PointerAlignment: Right |
120 changes: 120 additions & 0 deletions
120
linux/library/include/flutter_desktop_embedding/channels.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
// Copyright 2018 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
#ifndef LINUX_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_CHANNELS_H_ | ||
#define LINUX_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_CHANNELS_H_ | ||
|
||
#include <json/json.h> | ||
|
||
#include <memory> | ||
#include <string> | ||
|
||
#include <embedder.h> | ||
|
||
namespace flutter_desktop_embedding { | ||
|
||
// An object encapsulating a method call from Flutter. | ||
// TODO: Move serialization details into a method codec class, to match mobile | ||
// Flutter plugin APIs. | ||
class MethodCall { | ||
public: | ||
// Creates a MethodCall with the given name and, optionally, arguments. | ||
explicit MethodCall(const std::string &method_name, | ||
const Json::Value &arguments = Json::Value()); | ||
|
||
// Returns a new MethodCall created from a JSON message received from the | ||
// Flutter engine. | ||
static std::unique_ptr<MethodCall> CreateFromMessage( | ||
const Json::Value &message); | ||
~MethodCall(); | ||
|
||
// The name of the method being called. | ||
const std::string &method_name() const { return method_name_; } | ||
|
||
// The arguments to the method call, or a nullValue if there are none. | ||
const Json::Value &arguments() const { return arguments_; } | ||
|
||
// Returns a version of the method call serialized in the format expected by | ||
// the Flutter engine. | ||
Json::Value AsMessage() const; | ||
|
||
private: | ||
std::string method_name_; | ||
Json::Value arguments_; | ||
}; | ||
|
||
// Encapsulates a result sent back to the Flutter engine in response to a | ||
// MethodCall. Only one method should be called on any given instance. | ||
class MethodResult { | ||
public: | ||
// Sends a success response, indicating that the call completed successfully. | ||
// An optional value can be provided as part of the success message. | ||
void Success(const Json::Value &result = Json::Value()); | ||
|
||
// Sends an error response, indicating that the call was understood but | ||
// handling failed in some way. A string error code must be provided, and in | ||
// addition an optional user-readable error_message and/or details object can | ||
// be included. | ||
void Error(const std::string &error_code, | ||
const std::string &error_message = "", | ||
const Json::Value &error_details = Json::Value()); | ||
|
||
// Sends a not-implemented response, indicating that the method either was not | ||
// recognized, or has not been implemented. | ||
void NotImplemented(); | ||
|
||
protected: | ||
// Internal implementation of the interface methods above, to be implemented | ||
// in subclasses. | ||
virtual void SuccessInternal(const Json::Value &result) = 0; | ||
virtual void ErrorInternal(const std::string &error_code, | ||
const std::string &error_message, | ||
const Json::Value &error_details) = 0; | ||
virtual void NotImplementedInternal() = 0; | ||
}; | ||
|
||
// Implemention of MethodResult using JSON as the protocol. | ||
// TODO: Move this logic into method codecs. | ||
class JsonMethodResult : public MethodResult { | ||
public: | ||
// Creates a result object that will send results to |engine|, tagged as | ||
// associated with |response_handle|. The engine pointer must remain valid for | ||
// as long as this object exists. | ||
JsonMethodResult(FlutterEngine engine, | ||
const FlutterPlatformMessageResponseHandle *response_handle); | ||
~JsonMethodResult(); | ||
|
||
protected: | ||
void SuccessInternal(const Json::Value &result) override; | ||
void ErrorInternal(const std::string &error_code, | ||
const std::string &error_message, | ||
const Json::Value &error_details) override; | ||
void NotImplementedInternal() override; | ||
|
||
private: | ||
// Sends the given JSON response object to the engine. | ||
void SendResponseJson(const Json::Value &response); | ||
|
||
// Sends the given response data (which must either be nullptr or a serialized | ||
// JSON response) to the engine. | ||
// Uses a pointer rather than a reference since nullptr is used to indicate | ||
// not-implemented (which is represented to Flutter by no data). | ||
void SendResponse(const std::string *serialized_response); | ||
|
||
FlutterEngine engine_; | ||
const FlutterPlatformMessageResponseHandle *response_handle_; | ||
}; | ||
|
||
} // namespace flutter_desktop_embedding | ||
|
||
#endif // LINUX_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_CHANNELS_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 0 additions & 26 deletions
26
linux/library/include/flutter_desktop_embedding/common/platform_protocol.h
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
// Copyright 2018 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
#include <flutter_desktop_embedding/channels.h> | ||
|
||
#include <iostream> | ||
|
||
namespace flutter_desktop_embedding { | ||
|
||
constexpr char kMessageMethodKey[] = "method"; | ||
constexpr char kMessageArgumentsKey[] = "args"; | ||
|
||
MethodCall::MethodCall(const std::string &method_name, | ||
const Json::Value &arguments) | ||
: method_name_(method_name), arguments_(arguments) {} | ||
|
||
MethodCall::~MethodCall() {} | ||
|
||
std::unique_ptr<MethodCall> MethodCall::CreateFromMessage( | ||
const Json::Value &message) { | ||
Json::Value method = message[kMessageMethodKey]; | ||
if (method.isNull()) { | ||
return nullptr; | ||
} | ||
Json::Value arguments = message[kMessageArgumentsKey]; | ||
return std::make_unique<MethodCall>(method.asString(), arguments); | ||
} | ||
|
||
Json::Value MethodCall::AsMessage() const { | ||
Json::Value message(Json::objectValue); | ||
message[kMessageMethodKey] = method_name_; | ||
message[kMessageArgumentsKey] = arguments_; | ||
return message; | ||
} | ||
|
||
void MethodResult::Success(const Json::Value &result) { | ||
SuccessInternal(result); | ||
} | ||
|
||
void MethodResult::Error(const std::string &error_code, | ||
const std::string &error_message, | ||
const Json::Value &error_details) { | ||
ErrorInternal(error_code, error_message, error_details); | ||
} | ||
|
||
void MethodResult::NotImplemented() { NotImplementedInternal(); } | ||
|
||
JsonMethodResult::JsonMethodResult( | ||
FlutterEngine engine, | ||
const FlutterPlatformMessageResponseHandle *response_handle) | ||
: engine_(engine), response_handle_(response_handle) { | ||
if (!response_handle_) { | ||
std::cerr << "Error: Response handle must be provided for a response." | ||
<< std::endl; | ||
} | ||
} | ||
|
||
JsonMethodResult::~JsonMethodResult() { | ||
if (response_handle_) { | ||
// Warn, rather than send a not-implemented response, since the engine may | ||
// no longer be valid at this point. | ||
std::cerr | ||
<< "Warning: Failed to respond to a message. This is a memory leak." | ||
<< std::endl; | ||
} | ||
} | ||
|
||
void JsonMethodResult::SuccessInternal(const Json::Value &result) { | ||
Json::Value response(Json::arrayValue); | ||
response.append(result); | ||
SendResponseJson(response); | ||
} | ||
|
||
void JsonMethodResult::ErrorInternal(const std::string &error_code, | ||
const std::string &error_message, | ||
const Json::Value &error_details) { | ||
Json::Value response(Json::arrayValue); | ||
response.append(error_code); | ||
response.append(error_message.empty() ? Json::Value() : error_message); | ||
response.append(error_details); | ||
SendResponseJson(response); | ||
} | ||
|
||
void JsonMethodResult::NotImplementedInternal() { SendResponse(nullptr); } | ||
|
||
void JsonMethodResult::SendResponseJson(const Json::Value &response) { | ||
Json::StreamWriterBuilder writer_builder; | ||
std::string response_data = Json::writeString(writer_builder, response); | ||
SendResponse(&response_data); | ||
} | ||
|
||
void JsonMethodResult::SendResponse(const std::string *serialized_response) { | ||
if (!response_handle_) { | ||
std::cerr | ||
<< "Error: Response can be set only once. Ignoring duplicate response." | ||
<< std::endl; | ||
return; | ||
} | ||
|
||
const uint8_t *message_data = | ||
serialized_response | ||
? reinterpret_cast<const uint8_t *>(serialized_response->c_str()) | ||
: nullptr; | ||
size_t message_length = serialized_response ? serialized_response->size() : 0; | ||
FlutterEngineSendPlatformMessageResponse(engine_, response_handle_, | ||
message_data, message_length); | ||
// The engine frees the response handle once | ||
// FlutterEngineSendPlatformMessageResponse is called. | ||
response_handle_ = nullptr; | ||
} | ||
|
||
} // namespace flutter_desktop_embedding |
Oops, something went wrong.