mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
296 lines
10 KiB
C++
296 lines
10 KiB
C++
// 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.
|
|
|
|
#define RAPIDJSON_HAS_STDSTRING 1
|
|
|
|
#include "flutter/runtime/service_protocol.h"
|
|
|
|
#include <cstring>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "flutter/fml/posix_wrappers.h"
|
|
#include "flutter/fml/synchronization/waitable_event.h"
|
|
#include "rapidjson/stringbuffer.h"
|
|
#include "rapidjson/writer.h"
|
|
#include "third_party/dart/runtime/include/dart_tools_api.h"
|
|
|
|
namespace flutter {
|
|
|
|
const std::string_view ServiceProtocol::kScreenshotExtensionName =
|
|
"_flutter.screenshot";
|
|
const std::string_view ServiceProtocol::kScreenshotSkpExtensionName =
|
|
"_flutter.screenshotSkp";
|
|
const std::string_view ServiceProtocol::kRunInViewExtensionName =
|
|
"_flutter.runInView";
|
|
const std::string_view ServiceProtocol::kFlushUIThreadTasksExtensionName =
|
|
"_flutter.flushUIThreadTasks";
|
|
const std::string_view ServiceProtocol::kSetAssetBundlePathExtensionName =
|
|
"_flutter.setAssetBundlePath";
|
|
const std::string_view ServiceProtocol::kGetDisplayRefreshRateExtensionName =
|
|
"_flutter.getDisplayRefreshRate";
|
|
const std::string_view ServiceProtocol::kGetSkSLsExtensionName =
|
|
"_flutter.getSkSLs";
|
|
const std::string_view
|
|
ServiceProtocol::kEstimateRasterCacheMemoryExtensionName =
|
|
"_flutter.estimateRasterCacheMemory";
|
|
const std::string_view
|
|
ServiceProtocol::kRenderFrameWithRasterStatsExtensionName =
|
|
"_flutter.renderFrameWithRasterStats";
|
|
const std::string_view ServiceProtocol::kReloadAssetFonts =
|
|
"_flutter.reloadAssetFonts";
|
|
|
|
static constexpr std::string_view kViewIdPrefx = "_flutterView/";
|
|
static constexpr std::string_view kListViewsExtensionName =
|
|
"_flutter.listViews";
|
|
|
|
ServiceProtocol::ServiceProtocol()
|
|
: endpoints_({
|
|
// Private
|
|
kListViewsExtensionName,
|
|
|
|
// Public
|
|
kScreenshotExtensionName,
|
|
kScreenshotSkpExtensionName,
|
|
kRunInViewExtensionName,
|
|
kFlushUIThreadTasksExtensionName,
|
|
kSetAssetBundlePathExtensionName,
|
|
kGetDisplayRefreshRateExtensionName,
|
|
kGetSkSLsExtensionName,
|
|
kEstimateRasterCacheMemoryExtensionName,
|
|
kRenderFrameWithRasterStatsExtensionName,
|
|
kReloadAssetFonts,
|
|
}),
|
|
handlers_mutex_(fml::SharedMutex::Create()) {}
|
|
|
|
ServiceProtocol::~ServiceProtocol() {
|
|
ToggleHooks(false);
|
|
}
|
|
|
|
void ServiceProtocol::AddHandler(Handler* handler,
|
|
const Handler::Description& description) {
|
|
fml::UniqueLock lock(*handlers_mutex_);
|
|
handlers_.emplace(handler, description);
|
|
}
|
|
|
|
void ServiceProtocol::RemoveHandler(Handler* handler) {
|
|
fml::UniqueLock lock(*handlers_mutex_);
|
|
handlers_.erase(handler);
|
|
}
|
|
|
|
void ServiceProtocol::SetHandlerDescription(
|
|
Handler* handler,
|
|
const Handler::Description& description) {
|
|
fml::SharedLock lock(*handlers_mutex_);
|
|
auto it = handlers_.find(handler);
|
|
if (it != handlers_.end()) {
|
|
it->second.Store(description);
|
|
}
|
|
}
|
|
|
|
void ServiceProtocol::ToggleHooks(bool set) {
|
|
for (const auto& endpoint : endpoints_) {
|
|
Dart_RegisterIsolateServiceRequestCallback(
|
|
endpoint.data(), // method
|
|
&ServiceProtocol::HandleMessage, // callback
|
|
set ? this : nullptr // user data
|
|
);
|
|
}
|
|
}
|
|
|
|
static void WriteServerErrorResponse(rapidjson::Document* document,
|
|
const char* message) {
|
|
document->SetObject();
|
|
document->AddMember("code", -32000, document->GetAllocator());
|
|
rapidjson::Value message_value;
|
|
message_value.SetString(message, document->GetAllocator());
|
|
document->AddMember("message", message_value, document->GetAllocator());
|
|
}
|
|
|
|
bool ServiceProtocol::HandleMessage(const char* method,
|
|
const char** param_keys,
|
|
const char** param_values,
|
|
intptr_t num_params,
|
|
void* user_data,
|
|
const char** json_object) {
|
|
Handler::ServiceProtocolMap params;
|
|
for (intptr_t i = 0; i < num_params; i++) {
|
|
params[std::string_view{param_keys[i]}] = std::string_view{param_values[i]};
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
FML_DLOG(INFO) << "Service protcol method: " << method;
|
|
FML_DLOG(INFO) << "Arguments: " << params.size();
|
|
for (intptr_t i = 0; i < num_params; i++) {
|
|
FML_DLOG(INFO) << " " << i + 1 << ": " << param_keys[i] << " = "
|
|
<< param_values[i];
|
|
}
|
|
#endif // NDEBUG
|
|
|
|
rapidjson::Document document;
|
|
bool result = HandleMessage(std::string_view{method}, //
|
|
params, //
|
|
static_cast<ServiceProtocol*>(user_data), //
|
|
&document //
|
|
);
|
|
rapidjson::StringBuffer buffer;
|
|
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
|
document.Accept(writer);
|
|
*json_object = fml::strdup(buffer.GetString());
|
|
|
|
#ifndef NDEBUG
|
|
FML_DLOG(INFO) << "Response: " << *json_object;
|
|
FML_DLOG(INFO) << "RPC Result: " << result;
|
|
#endif // NDEBUG
|
|
|
|
return result;
|
|
}
|
|
|
|
bool ServiceProtocol::HandleMessage(std::string_view method,
|
|
const Handler::ServiceProtocolMap& params,
|
|
ServiceProtocol* service_protocol,
|
|
rapidjson::Document* response) {
|
|
if (service_protocol == nullptr) {
|
|
WriteServerErrorResponse(response, "Service protocol unavailable.");
|
|
return false;
|
|
}
|
|
|
|
return service_protocol->HandleMessage(method, params, response);
|
|
}
|
|
|
|
[[nodiscard]] static bool HandleMessageOnHandler(
|
|
ServiceProtocol::Handler* handler,
|
|
std::string_view method,
|
|
const ServiceProtocol::Handler::ServiceProtocolMap& params,
|
|
rapidjson::Document* document) {
|
|
FML_DCHECK(handler);
|
|
fml::AutoResetWaitableEvent latch;
|
|
bool result = false;
|
|
fml::TaskRunner::RunNowOrPostTask(
|
|
handler->GetServiceProtocolHandlerTaskRunner(method),
|
|
[&latch, //
|
|
&result, //
|
|
&handler, //
|
|
&method, //
|
|
¶ms, //
|
|
&document //
|
|
]() {
|
|
result =
|
|
handler->HandleServiceProtocolMessage(method, params, document);
|
|
latch.Signal();
|
|
});
|
|
latch.Wait();
|
|
return result;
|
|
}
|
|
|
|
bool ServiceProtocol::HandleMessage(std::string_view method,
|
|
const Handler::ServiceProtocolMap& params,
|
|
rapidjson::Document* response) const {
|
|
if (method == kListViewsExtensionName) {
|
|
// So far, this is the only built-in method that does not forward to the
|
|
// dynamic set of handlers.
|
|
return HandleListViewsMethod(response);
|
|
}
|
|
|
|
fml::SharedLock lock(*handlers_mutex_);
|
|
|
|
if (handlers_.empty()) {
|
|
WriteServerErrorResponse(response,
|
|
"There are no running service protocol handlers.");
|
|
return false;
|
|
}
|
|
|
|
// Find the handler by its "viewId" in the params.
|
|
auto view_id_param_found = params.find(std::string_view{"viewId"});
|
|
if (view_id_param_found != params.end()) {
|
|
auto* handler = reinterpret_cast<Handler*>(std::stoull(
|
|
view_id_param_found->second.data() + kViewIdPrefx.size(), nullptr, 16));
|
|
auto handler_found = handlers_.find(handler);
|
|
if (handler_found != handlers_.end()) {
|
|
return HandleMessageOnHandler(handler, method, params, response);
|
|
}
|
|
}
|
|
|
|
// Handle legacy calls that do not specify a handler in their args.
|
|
// TODO(chinmaygarde): Deprecate these calls in the tools and remove these
|
|
// fallbacks.
|
|
if (method == kScreenshotExtensionName ||
|
|
method == kScreenshotSkpExtensionName ||
|
|
method == kFlushUIThreadTasksExtensionName) {
|
|
return HandleMessageOnHandler(handlers_.begin()->first, method, params,
|
|
response);
|
|
}
|
|
|
|
WriteServerErrorResponse(
|
|
response,
|
|
"Service protocol could not handle or find a handler for the "
|
|
"requested method.");
|
|
return false;
|
|
}
|
|
|
|
static std::string CreateFlutterViewID(intptr_t handler) {
|
|
std::stringstream stream;
|
|
stream << kViewIdPrefx << "0x" << std::hex << handler;
|
|
return stream.str();
|
|
}
|
|
|
|
static std::string CreateIsolateID(int64_t isolate) {
|
|
std::stringstream stream;
|
|
stream << "isolates/" << isolate;
|
|
return stream.str();
|
|
}
|
|
|
|
void ServiceProtocol::Handler::Description::Write(
|
|
Handler* handler,
|
|
rapidjson::Value& view,
|
|
rapidjson::MemoryPoolAllocator<>& allocator) const {
|
|
view.SetObject();
|
|
view.AddMember("type", "FlutterView", allocator);
|
|
view.AddMember("id", CreateFlutterViewID(reinterpret_cast<intptr_t>(handler)),
|
|
allocator);
|
|
if (isolate_port != 0) {
|
|
rapidjson::Value isolate(rapidjson::Type::kObjectType);
|
|
{
|
|
isolate.AddMember("type", "@Isolate", allocator);
|
|
isolate.AddMember("fixedId", true, allocator);
|
|
isolate.AddMember("id", CreateIsolateID(isolate_port), allocator);
|
|
isolate.AddMember("name", isolate_name, allocator);
|
|
isolate.AddMember("number", isolate_port, allocator);
|
|
}
|
|
view.AddMember("isolate", isolate, allocator);
|
|
}
|
|
}
|
|
|
|
bool ServiceProtocol::HandleListViewsMethod(
|
|
rapidjson::Document* response) const {
|
|
fml::SharedLock lock(*handlers_mutex_);
|
|
std::vector<std::pair<intptr_t, Handler::Description>> descriptions;
|
|
for (const auto& handler : handlers_) {
|
|
descriptions.emplace_back(reinterpret_cast<intptr_t>(handler.first),
|
|
handler.second.Load());
|
|
}
|
|
|
|
auto& allocator = response->GetAllocator();
|
|
|
|
// Construct the response objects.
|
|
response->SetObject();
|
|
response->AddMember("type", "FlutterViewList", allocator);
|
|
|
|
rapidjson::Value viewsList(rapidjson::Type::kArrayType);
|
|
for (const auto& description : descriptions) {
|
|
rapidjson::Value view(rapidjson::Type::kObjectType);
|
|
description.second.Write(reinterpret_cast<Handler*>(description.first),
|
|
view, allocator);
|
|
viewsList.PushBack(view, allocator);
|
|
}
|
|
|
|
response->AddMember("views", viewsList, allocator);
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace flutter
|