flutter_flutter/runtime/service_protocol.cc
Chinmay Garde eec74e5c92
Rename the blink namespace to flutter. (#8517)
Some components in the Flutter engine were derived from the forked blink codebase. While the forked components have either been removed or rewritten, the use of the blink namespace has mostly (and inconsistently) remained. This renames the blink namesapce to flutter for consistency. There are no functional changes in this patch.
2019-04-09 12:44:42 -07:00

280 lines
9.4 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 <string.h>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#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 fml::StringView ServiceProtocol::kScreenshotExtensionName =
"_flutter.screenshot";
const fml::StringView ServiceProtocol::kScreenshotSkpExtensionName =
"_flutter.screenshotSkp";
const fml::StringView ServiceProtocol::kRunInViewExtensionName =
"_flutter.runInView";
const fml::StringView ServiceProtocol::kFlushUIThreadTasksExtensionName =
"_flutter.flushUIThreadTasks";
const fml::StringView ServiceProtocol::kSetAssetBundlePathExtensionName =
"_flutter.setAssetBundlePath";
const fml::StringView ServiceProtocol::kGetDisplayRefreshRateExtensionName =
"_flutter.getDisplayRefreshRate";
static constexpr fml::StringView kViewIdPrefx = "_flutterView/";
static constexpr fml::StringView kListViewsExtensionName = "_flutter.listViews";
ServiceProtocol::ServiceProtocol()
: endpoints_({
// Private
kListViewsExtensionName,
// Public
kScreenshotExtensionName,
kScreenshotSkpExtensionName,
kRunInViewExtensionName,
kFlushUIThreadTasksExtensionName,
kSetAssetBundlePathExtensionName,
kGetDisplayRefreshRateExtensionName,
}),
handlers_mutex_(fml::SharedMutex::Create()) {}
ServiceProtocol::~ServiceProtocol() {
ToggleHooks(false);
}
void ServiceProtocol::AddHandler(Handler* handler,
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,
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[fml::StringView{param_keys[i]}] = fml::StringView{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(fml::StringView{method}, //
params, //
static_cast<ServiceProtocol*>(user_data), //
document //
);
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
document.Accept(writer);
*json_object = strdup(buffer.GetString());
#ifndef NDEBUG
FML_DLOG(INFO) << "Response: " << *json_object;
FML_DLOG(INFO) << "RPC Result: " << result;
#endif // NDEBUG
return result;
}
bool ServiceProtocol::HandleMessage(fml::StringView 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);
}
FML_WARN_UNUSED_RESULT
static bool HandleMessageOnHandler(
ServiceProtocol::Handler* handler,
fml::StringView 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, //
&params, //
&document //
]() {
result =
handler->HandleServiceProtocolMessage(method, params, document);
latch.Signal();
});
latch.Wait();
return result;
}
bool ServiceProtocol::HandleMessage(fml::StringView 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_.size() == 0) {
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(fml::StringView{"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