blob: d1151c1bd592f17da91b3a7c0b59b1e85506d0ef [file] [log] [blame]
// Copyright 2016 The Chromium 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/common/platform_view_service_protocol.h"
#include <string.h>
#include <string>
#include <vector>
#include "flutter/common/threads.h"
#include "flutter/shell/common/picture_serializer.h"
#include "flutter/shell/common/rasterizer.h"
#include "flutter/shell/common/shell.h"
#include "lib/ftl/memory/weak_ptr.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/src/utils/SkBase64.h"
namespace shell {
namespace {
constexpr char kViewIdPrefx[] = "_flutterView/";
constexpr size_t kViewIdPrefxLength = sizeof(kViewIdPrefx) - 1;
static intptr_t KeyIndex(const char** param_keys,
intptr_t num_params,
const char* key) {
if (param_keys == NULL) {
return -1;
}
for (intptr_t i = 0; i < num_params; i++) {
if (strcmp(param_keys[i], key) == 0) {
return i;
}
}
return -1;
}
static const char* ValueForKey(const char** param_keys,
const char** param_values,
intptr_t num_params,
const char* key) {
intptr_t index = KeyIndex(param_keys, num_params, key);
if (index < 0) {
return NULL;
}
return param_values[index];
}
static bool ErrorMissingParameter(const char** json_object, const char* name) {
const intptr_t kInvalidParams = -32602;
std::stringstream response;
response << "{\"code\":" << std::to_string(kInvalidParams) << ",";
response << "\"message\":\"Invalid params\",";
response << "\"data\": {\"details\": \"" << name << "\"}}";
*json_object = strdup(response.str().c_str());
return false;
}
static bool ErrorBadParameter(const char** json_object,
const char* name,
const char* value) {
const intptr_t kInvalidParams = -32602;
std::stringstream response;
response << "{\"code\":" << std::to_string(kInvalidParams) << ",";
response << "\"message\":\"Invalid params\",";
response << "\"data\": {\"details\": \"parameter: " << name << " has a bad ";
response << "value: " << value << "\"}}";
*json_object = strdup(response.str().c_str());
return false;
}
static bool ErrorUnknownView(const char** json_object, const char* view_id) {
const intptr_t kInvalidParams = -32602;
std::stringstream response;
response << "{\"code\":" << std::to_string(kInvalidParams) << ",";
response << "\"message\":\"Invalid params\",";
response << "\"data\": {\"details\": \"view not found: " << view_id << "\"}}";
*json_object = strdup(response.str().c_str());
return false;
}
static bool ErrorServer(const char** json_object, const char* message) {
const intptr_t kServerError = -32000;
std::stringstream response;
response << "{\"code\":" << std::to_string(kServerError) << ",";
response << "\"message\":\"" << message << "\"}";
*json_object = strdup(response.str().c_str());
return false;
}
static void AppendIsolateRef(std::stringstream* stream,
int64_t main_port,
const std::string name) {
*stream << "{\"type\":\"@Isolate\",\"fixedId\":true,\"id\":\"isolates/";
*stream << main_port << "\",\"name\":\"" << name << "\",";
*stream << "\"number\":\"" << main_port << "\"}";
}
static void AppendFlutterView(std::stringstream* stream,
uintptr_t view_id,
int64_t isolate_id,
const std::string isolate_name) {
*stream << "{\"type\":\"FlutterView\", \"id\": \"" << kViewIdPrefx << "0x"
<< std::hex << view_id << std::dec << "\"";
if (isolate_id != ILLEGAL_PORT) {
// Append the isolate (if it exists).
*stream << ","
<< "\"isolate\":";
AppendIsolateRef(stream, isolate_id, isolate_name);
}
*stream << "}";
}
} // namespace
void PlatformViewServiceProtocol::RegisterHook(bool running_precompiled_code) {
// Listing of FlutterViews.
Dart_RegisterRootServiceRequestCallback(kListViewsExtensionName, &ListViews,
nullptr);
// Screenshot.
Dart_RegisterRootServiceRequestCallback(kScreenshotExtensionName, &Screenshot,
nullptr);
// The following set of service protocol extensions require debug build
if (running_precompiled_code) {
return;
}
Dart_RegisterRootServiceRequestCallback(kRunInViewExtensionName, &RunInView,
nullptr);
}
const char* PlatformViewServiceProtocol::kRunInViewExtensionName =
"_flutter.runInView";
bool PlatformViewServiceProtocol::RunInView(const char* method,
const char** param_keys,
const char** param_values,
intptr_t num_params,
void* user_data,
const char** json_object) {
const char* view_id =
ValueForKey(param_keys, param_values, num_params, "viewId");
const char* asset_directory =
ValueForKey(param_keys, param_values, num_params, "assetDirectory");
const char* main_script =
ValueForKey(param_keys, param_values, num_params, "mainScript");
const char* packages_file =
ValueForKey(param_keys, param_values, num_params, "packagesFile");
if (view_id == NULL) {
return ErrorMissingParameter(json_object, "viewId");
}
if (strncmp(view_id, kViewIdPrefx, kViewIdPrefxLength) != 0) {
return ErrorBadParameter(json_object, "viewId", view_id);
}
if (asset_directory == NULL) {
return ErrorMissingParameter(json_object, "assetDirectory");
}
if (main_script == NULL) {
return ErrorMissingParameter(json_object, "mainScript");
}
if (packages_file == NULL) {
return ErrorMissingParameter(json_object, "packagesFile");
}
// Convert the actual flutter view hex id into a number.
uintptr_t view_id_as_num =
std::stoull((view_id + kViewIdPrefxLength), nullptr, 16);
// Ask the Shell to run this script in the specified view. This will run a
// task on the UI thread before returning.
Shell& shell = Shell::Shared();
bool view_existed = false;
Dart_Port main_port = ILLEGAL_PORT;
std::string isolate_name;
shell.RunInPlatformView(view_id_as_num, main_script, packages_file,
asset_directory, &view_existed, &main_port,
&isolate_name);
if (!view_existed) {
// If the view did not exist this request has definitely failed.
return ErrorUnknownView(json_object, view_id);
} else {
// The view existed and the isolate was created. Success.
std::stringstream response;
response << "{\"type\":\"Success\","
<< "\"view\":";
AppendFlutterView(&response, view_id_as_num, main_port, isolate_name);
response << "}";
*json_object = strdup(response.str().c_str());
return true;
}
return true;
}
const char* PlatformViewServiceProtocol::kListViewsExtensionName =
"_flutter.listViews";
bool PlatformViewServiceProtocol::ListViews(const char* method,
const char** param_keys,
const char** param_values,
intptr_t num_params,
void* user_data,
const char** json_object) {
// Ask the Shell for the list of platform views. This will run a task on
// the UI thread before returning.
Shell& shell = Shell::Shared();
std::vector<Shell::PlatformViewInfo> platform_views;
shell.WaitForPlatformViewIds(&platform_views);
std::stringstream response;
response << "{\"type\":\"FlutterViewList\",\"views\":[";
bool prefix_comma = false;
for (auto it = platform_views.begin(); it != platform_views.end(); it++) {
uintptr_t view_id = it->view_id;
int64_t isolate_id = it->isolate_id;
const std::string& isolate_name = it->isolate_name;
if (!view_id) {
continue;
}
if (prefix_comma) {
response << ',';
} else {
prefix_comma = true;
}
AppendFlutterView(&response, view_id, isolate_id, isolate_name);
}
response << "]}";
// Copy the response.
*json_object = strdup(response.str().c_str());
return true;
}
const char* PlatformViewServiceProtocol::kScreenshotExtensionName =
"_flutter.screenshot";
static sk_sp<SkData> EncodeBitmapAsPNG(const SkBitmap& bitmap) {
if (bitmap.empty()) {
return nullptr;
}
SkPixmap pixmap;
if (!bitmap.peekPixels(&pixmap)) {
return nullptr;
}
PngPixelSerializer serializer;
sk_sp<SkData> data(serializer.encode(pixmap));
return data;
}
bool PlatformViewServiceProtocol::Screenshot(const char* method,
const char** param_keys,
const char** param_values,
intptr_t num_params,
void* user_data,
const char** json_object) {
ftl::AutoResetWaitableEvent latch;
SkBitmap bitmap;
blink::Threads::Gpu()->PostTask([&latch, &bitmap]() {
ScreenshotGpuTask(&bitmap);
latch.Signal();
});
latch.Wait();
sk_sp<SkData> png(EncodeBitmapAsPNG(bitmap));
if (!png)
return ErrorServer(json_object, "can not encode screenshot");
size_t b64_size = SkBase64::Encode(png->data(), png->size(), nullptr);
SkAutoTMalloc<char> b64_data(b64_size);
SkBase64::Encode(png->data(), png->size(), b64_data.get());
std::stringstream response;
response << "{\"type\":\"Screenshot\","
<< "\"screenshot\":\"" << std::string{b64_data.get(), b64_size}
<< "\"}";
*json_object = strdup(response.str().c_str());
return true;
}
void PlatformViewServiceProtocol::ScreenshotGpuTask(SkBitmap* bitmap) {
std::vector<ftl::WeakPtr<Rasterizer>> rasterizers;
Shell::Shared().GetRasterizers(&rasterizers);
if (rasterizers.size() != 1)
return;
Rasterizer* rasterizer = rasterizers[0].get();
if (rasterizer == nullptr)
return;
flow::LayerTree* layer_tree = rasterizer->GetLastLayerTree();
if (layer_tree == nullptr)
return;
const SkISize& frame_size = layer_tree->frame_size();
if (!bitmap->tryAllocN32Pixels(frame_size.width(), frame_size.height()))
return;
sk_sp<SkSurface> surface = SkSurface::MakeRasterDirect(
bitmap->info(), bitmap->getPixels(), bitmap->rowBytes());
flow::CompositorContext compositor_context(nullptr);
SkCanvas* canvas = surface->getCanvas();
flow::CompositorContext::ScopedFrame frame =
compositor_context.AcquireFrame(nullptr, canvas, false);
canvas->clear(SK_ColorBLACK);
layer_tree->Raster(frame);
canvas->flush();
}
} // namespace shell